Forráskód Böngészése

Import S928BXXS4AXJB audio-kernel changes

Change-Id: I3d56a872209378068d0eea3caf04f2ea7c5a81d4
David Wronek 3 hónapja
szülő
commit
3563ee22f6
28 módosított fájl, 8688 hozzáadás és 15 törlés
  1. 12 0
      qcom/opensource/audio-kernel/Android.mk
  2. 2 0
      qcom/opensource/audio-kernel/EnableBazel.mk
  3. 5 0
      qcom/opensource/audio-kernel/asoc/codecs/Kbuild
  4. 106 11
      qcom/opensource/audio-kernel/asoc/codecs/lpass-cdc/lpass-cdc-tx-macro.c
  5. 87 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/Kbuild
  6. 107 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/Kconfig
  7. 53 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/Makefile
  8. 30 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-codec.h
  9. 22 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-device.h
  10. 43 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-ext.h
  11. 42 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-logic.h
  12. 38 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-misc.h
  13. 120 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-regbin-parser.h
  14. 26 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-regmap.h
  15. 420 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx.h
  16. 716 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/src/tas25xx-codec.c
  17. 1472 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/src/tas25xx-logic.c
  18. 497 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/src/tas25xx-misc.c
  19. 2495 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/src/tas25xx-regbin-parser.c
  20. 1121 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/src/tas25xx-regmap.c
  21. 212 0
      qcom/opensource/audio-kernel/asoc/codecs/tas25xx/src/tas25xx.c
  22. 27 0
      qcom/opensource/audio-kernel/asoc/msm_common.c
  23. 19 0
      qcom/opensource/audio-kernel/asoc/msm_common.h
  24. 34 0
      qcom/opensource/audio-kernel/asoc/msm_dailink.h
  25. 922 2
      qcom/opensource/audio-kernel/asoc/pineapple.c
  26. 3 0
      qcom/opensource/audio-kernel/audio_kernel_modules.mk
  27. 3 1
      qcom/opensource/audio-kernel/audio_kernel_product_board.mk
  28. 54 1
      qcom/opensource/audio-kernel/dsp/adsp-loader.c

+ 12 - 0
qcom/opensource/audio-kernel/Android.mk

@@ -569,7 +569,19 @@ LOCAL_MODULE_DEBUG_ENABLE := true
 LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
 include $(DLKM_DIR)/Build_external_kernelmodule.mk
 endif
+ifeq ($(PROJECT_NAME),$(filter $(PROJECT_NAME),q6q b6q q6aq))
 ##########################################################
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(AUDIO_SRC_FILES)
+LOCAL_MODULE := tas25xx_dlkm.ko
+LOCAL_MODULE_KBUILD_NAME := asoc/codecs/tas25xx/tas25xx_dlkm.ko
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_DEBUG_ENABLE := true
+LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT)
+include $(DLKM_DIR)/Build_external_kernelmodule.mk
+endif
+###########################################################
+
 endif # DLKM check
 endif # supported target check
 endif

+ 2 - 0
qcom/opensource/audio-kernel/EnableBazel.mk

@@ -40,6 +40,8 @@ LOCAL_MODULE_KO_DIRS += asoc/codecs/wcd939x/wcd939x_slave_dlkm.ko
 LOCAL_MODULE_KO_DIRS += asoc/codecs/wcd9378/wcd9378_dlkm.ko
 LOCAL_MODULE_KO_DIRS += asoc/codecs/wcd9378/wcd9378_slave_dlkm.ko
 LOCAL_MODULE_KO_DIRS += asoc/codecs/hdmi_dlkm.ko
+ifeq ($(PROJECT_NAME),$(filter $(PROJECT_NAME),q6q b6q q6aq))
+LOCAL_MODULE_KO_DIRS += asoc/codecs/tas25xx/tas25xx_dlkm.ko
 endif
 
 ifeq ($(call is-board-platform-in-list,pitti),true)

+ 5 - 0
qcom/opensource/audio-kernel/asoc/codecs/Kbuild

@@ -285,6 +285,11 @@ ifeq ($(KERNEL_BUILD), 1)
 	obj-y	+= rouleur/
 	obj-y	+= ./
 endif
+
+ifdef CONFIG_SND_SOC_TAS25XX
+	obj-y	+= tas25xx/
+endif
+
 # Module information used by KBuild framework
 obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o
 obj-$(CONFIG_WCD9XXX_CODEC_CORE_V2) += wcd_core_dlkm.o

+ 106 - 11
qcom/opensource/audio-kernel/asoc/codecs/lpass-cdc/lpass-cdc-tx-macro.c

@@ -17,6 +17,9 @@
 #include "lpass-cdc.h"
 #include "lpass-cdc-registers.h"
 #include "lpass-cdc-clk-rsc.h"
+#if IS_ENABLED(CONFIG_SND_SOC_SAMSUNG_AUDIO)
+#include <sound/samsung/snd_debug_proc.h>
+#endif
 
 #define AUTO_SUSPEND_DELAY  50 /* delay in msec */
 #define LPASS_CDC_TX_MACRO_MAX_OFFSET 0x1000
@@ -104,6 +107,19 @@ enum {
 	VA_MCLK,
 };
 
+/* Based on 9.6MHZ MCLK Freq */
+enum {
+	CLK_DISABLED = 0,
+	CLK_2P4MHZ,
+	CLK_0P6MHZ,
+};
+
+static int dmic_clk_rate_div[] = {
+	[CLK_DISABLED] = 0,
+	[CLK_2P4MHZ] = LPASS_CDC_TX_MACRO_CLK_DIV_4,
+	[CLK_0P6MHZ] = LPASS_CDC_TX_MACRO_CLK_DIV_16,
+};
+
 struct lpass_cdc_tx_macro_reg_mask_val {
 	u16 reg;
 	u8 mask;
@@ -149,8 +165,15 @@ struct lpass_cdc_tx_macro_priv {
 	int pcm_rate[NUM_DECIMATORS];
 	bool swr_dmic_enable;
 	int wlock_holders;
+	u32 dmic_rate_override;
+};
+
+static const char* const dmic_rate_override_text[] = {
+	"DISABLED", "CLK_2P4MHZ", "CLK_0P6MHZ"
 };
 
+static SOC_ENUM_SINGLE_EXT_DECL(dmic_rate_enum, dmic_rate_override_text);
+
 static int lpass_cdc_tx_macro_wake_enable(struct lpass_cdc_tx_macro_priv *tx_priv,
 					bool wake_enable)
 {
@@ -202,12 +225,48 @@ static bool lpass_cdc_tx_macro_get_data(struct snd_soc_component *component,
 	return true;
 }
 
+static int lpass_cdc_dmic_rate_override_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct lpass_cdc_tx_macro_priv *tx_priv = NULL;
+	struct device *tx_dev = NULL;
+
+	if (!lpass_cdc_tx_macro_get_data(component, &tx_dev, &tx_priv, __func__))
+		return -EINVAL;
+
+	ucontrol->value.enumerated.item[0] = tx_priv->dmic_rate_override;
+	dev_dbg(component->dev, "%s: dmic rate: %d\n",
+		__func__, tx_priv->dmic_rate_override);
+
+	return 0;
+}
+
+static int lpass_cdc_dmic_rate_override_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct lpass_cdc_tx_macro_priv *tx_priv = NULL;
+	struct device *tx_dev = NULL;
+
+	if (!lpass_cdc_tx_macro_get_data(component, &tx_dev, &tx_priv, __func__))
+		return -EINVAL;
+
+	tx_priv->dmic_rate_override = ucontrol->value.enumerated.item[0];
+	dev_dbg(component->dev, "%s: dmic rate: %d\n",
+		__func__, tx_priv->dmic_rate_override);
+
+	return 0;
+}
+
 static int lpass_cdc_tx_macro_mclk_enable(
 				struct lpass_cdc_tx_macro_priv *tx_priv,
 				bool mclk_enable)
 {
 	struct regmap *regmap = dev_get_regmap(tx_priv->dev->parent, NULL);
-	int ret = 0;
+	int ret = 0, rc = 0;
 
 	if (regmap == NULL) {
 		dev_err_ratelimited(tx_priv->dev, "%s: regmap is NULL\n", __func__);
@@ -232,16 +291,34 @@ static int lpass_cdc_tx_macro_mclk_enable(
 		lpass_cdc_clk_rsc_fs_gen_request(tx_priv->dev,
 					true);
 		regcache_mark_dirty(regmap);
-		regcache_sync_region(regmap,
+
+		ret = regcache_sync_region(regmap,
 				TX_START_OFFSET,
 				TX_MAX_OFFSET);
+		if (ret < 0) {
+			dev_err_ratelimited(tx_priv->dev,
+				"%s: regcache_sync_region failed\n",
+				__func__);
+#if IS_ENABLED(CONFIG_SND_SOC_SAMSUNG_AUDIO)
+			sdp_info_print("%s: regcache_sync_region failed\n",
+				__func__);
+#endif
+			lpass_cdc_clk_rsc_fs_gen_request(tx_priv->dev,
+					false);
+			lpass_cdc_clk_rsc_request_clock(tx_priv->dev,
+						TX_CORE_CLK,
+						TX_CORE_CLK,
+						false);
+			goto exit;
+		}
 		if (tx_priv->tx_mclk_users == 0) {
-			regmap_update_bits(regmap,
+			rc = (rc | regmap_update_bits(regmap,
 				LPASS_CDC_TX_CLK_RST_CTRL_MCLK_CONTROL,
-				0x01, 0x01);
-			regmap_update_bits(regmap,
+				0x01, 0x01));
+
+			rc = (rc | regmap_update_bits(regmap,
 				LPASS_CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL,
-				0x01, 0x01);
+				0x01, 0x01));
 		}
 		tx_priv->tx_mclk_users++;
 	} else {
@@ -253,17 +330,26 @@ static int lpass_cdc_tx_macro_mclk_enable(
 		}
 		tx_priv->tx_mclk_users--;
 		if (tx_priv->tx_mclk_users == 0) {
-			regmap_update_bits(regmap,
+			rc = (rc | regmap_update_bits(regmap,
 				LPASS_CDC_TX_CLK_RST_CTRL_FS_CNT_CONTROL,
-				0x01, 0x00);
-			regmap_update_bits(regmap,
+				0x01, 0x00));
+			rc = (rc | regmap_update_bits(regmap,
 				LPASS_CDC_TX_CLK_RST_CTRL_MCLK_CONTROL,
-				0x01, 0x00);
+				0x01, 0x00));
 		}
+		
+		if (rc < 0) {
+			dev_err_ratelimited(tx_priv->dev, "%s: register writes failed\n",
+				__func__);
+#if IS_ENABLED(CONFIG_SND_SOC_SAMSUNG_AUDIO)
+			sdp_info_print("%s: register writes failed\n",
+				__func__);
+#endif
+		}			
 
 		lpass_cdc_clk_rsc_fs_gen_request(tx_priv->dev,
 				false);
-		lpass_cdc_clk_rsc_request_clock(tx_priv->dev,
+		ret = lpass_cdc_clk_rsc_request_clock(tx_priv->dev,
 				 TX_CORE_CLK,
 				 TX_CORE_CLK,
 				 false);
@@ -1869,6 +1955,9 @@ static const struct snd_kcontrol_new lpass_cdc_tx_macro_snd_controls[] = {
 
 	SOC_ENUM_EXT("BCS CH_SEL", bcs_ch_sel_mux_enum,
 		     lpass_cdc_tx_macro_get_bcs_ch_sel, lpass_cdc_tx_macro_put_bcs_ch_sel),
+
+	SOC_ENUM_EXT("DMIC_RATE OVERRIDE", dmic_rate_enum,
+			lpass_cdc_dmic_rate_override_get, lpass_cdc_dmic_rate_override_put),
 };
 
 static int lpass_cdc_tx_macro_clk_div_get(struct snd_soc_component *component)
@@ -1879,6 +1968,9 @@ static int lpass_cdc_tx_macro_clk_div_get(struct snd_soc_component *component)
 	if (!lpass_cdc_tx_macro_get_data(component, &tx_dev, &tx_priv, __func__))
 		return -EINVAL;
 
+	if (tx_priv->dmic_rate_override)
+		return dmic_clk_rate_div[tx_priv->dmic_rate_override];
+		
 	return (int)tx_priv->dmic_clk_div;
 }
 
@@ -1935,6 +2027,9 @@ undefined_rate:
 static const struct lpass_cdc_tx_macro_reg_mask_val
 				lpass_cdc_tx_macro_reg_init[] = {
 	{LPASS_CDC_TX0_TX_PATH_SEC7, 0x3F, 0x0A},
+	{LPASS_CDC_TX0_TX_PATH_CFG1, 0x0F, 0x0A},
+	{LPASS_CDC_TX1_TX_PATH_CFG1, 0x0F, 0x0A},
+	{LPASS_CDC_TX2_TX_PATH_CFG1, 0x0F, 0x0A},
 };
 
 static int lpass_cdc_tx_macro_init(struct snd_soc_component *component)

+ 87 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/Kbuild

@@ -0,0 +1,87 @@
+ifeq ($(MODNAME),)
+	KERNEL_BUILD := 1
+else
+	KERNEL_BUILD := 0
+endif
+
+ifeq ($(KERNEL_BUILD), 1)
+	AUDIO_BLD_DIR := $(srctree)
+	AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio
+endif
+
+
+ifeq ($(KERNEL_BUILD), 0)
+	ifeq ($(CONFIG_ARCH_WAIPIO), y)
+		include $(AUDIO_ROOT)/config/waipioauto.conf
+		INCS    +=  -include $(AUDIO_ROOT)/config/waipioautoconf.h
+	endif
+endif
+
+############ COMMON ############
+COMMON_DIR :=	include
+COMMON_INC :=	-I$(AUDIO_ROOT)/$(COMMON_DIR)
+
+ifdef CONFIG_SND_SOC_TAS25XX
+	TAS25XX_OBJS += src/tas25xx-codec.o
+	TAS25XX_OBJS += src/tas25xx-regmap.o
+	TAS25XX_OBJS += src/tas25xx.o
+	TAS25XX_OBJS += src/tas25xx-logic.o
+	TAS25XX_OBJS += src/tas25xx-regbin-parser.o
+endif
+
+
+ifneq ($(CONFIG_TAS25XX_ALGO),)
+TAS25XX_OBJS += algo/src/tas25xx-algo-intf.o
+TAS25XX_OBJS += algo/src/tas25xx-calib.o
+TAS25XX_OBJS += algo/src/tas25xx-algo-common.o
+endif
+
+ifneq ($(CONFIG_PLATFORM_MTK),)
+TAS25XX_OBJS += algo/platform/mtk/tas25xx-mtk-wrapper.o
+endif
+
+ifneq ($(CONFIG_TAS25XX_MISC),)
+TAS25XX_OBJS += src/tas25xx-misc.o
+endif
+
+ifneq ($(CONFIG_PLATFORM_QCOM),)
+TAS25XX_OBJS += algo/platform/qcom/tas25xx-algo-qdsp-intf.o
+endif
+
+ifneq ($(CONFIG_TISA_KBIN_INTF),)
+TAS25XX_OBJS += algo/src/tas25xx-algo-kbin-intf.o
+TAS25XX_OBJS += algo/src/tas25xx-algo-bin-utils.o
+endif
+
+ifneq ($(CONFIG_TAS25XX_CALIB_VAL_BIG),)
+TAS25XX_OBJS += algo/src/tas25xx-calib-validation.o
+endif
+
+ifneq ($(CONFIG_TISA_SYSFS_INTF),)
+TAS25XX_OBJS += algo/src/tas25xx-sysfs-debugfs-utils.o
+TAS25XX_OBJS += algo/src/smartpa-debug-common.o
+endif
+
+ifneq ($(CONFIG_TISA_BIN_INTF),)
+TAS25XX_OBJS += algo/src/tas25xx-algo-bin-intf.o
+endif
+
+ifneq ($(CONFIG_PLATFORM_EXYNOS),)
+TAS25XX_OBJS += algo/platform/exynos/tas25xx-algo-exynos-dsp-intf.o
+TAS25XX_OBJS += algo/platform/exynos/skinprot-sysfs-cb.o
+endif
+
+LINUX_INC += -Iinclude/linux
+TAS25XX_INC = -I$(AUDIO_ROOT)/asoc/codecs/tas25xx
+TAS25XX_INC += -I$(AUDIO_ROOT)/asoc/codecs/tas25xx/inc
+INCS += $(COMMON_INC) $(TAS25XX_INC)
+EXTRA_CFLAGS += $(INCS)
+
+
+# Module information used by KBuild framework
+obj-$(CONFIG_SND_SOC_TAS25XX) += tas25xx_dlkm.o
+tas25xx_dlkm-y := $(TAS25XX_OBJS)
+
+# inject some build related information
+DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\"
+

+ 107 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/Kconfig

@@ -0,0 +1,107 @@
+
+menuconfig SND_SOC_TAS25XX
+    tristate "Texas Instruments TAS25XX SmartAmp(R)"
+    help
+      Enable the support for TAS driver.
+
+      This is the main config under which various
+      other configuration can be enabled based on
+      the below configurations.
+
+if SND_SOC_TAS25XX
+config PLATFORM_EXYNOS
+    bool "Exynos platform support"
+    depends on SND_SOC_TAS25XX
+    default n
+    help
+      Enable support for Exynos platform.
+
+      This enables the platform specific interface
+      which acts as abstraction layer to the algorithm
+      for Exynos platform.
+
+config PLATFORM_MTK
+    bool "MTK platform support"
+    depends on SND_SOC_TAS25XX
+    default n
+    help
+      Enable support for MTK platform.
+
+      This enables the platform specific interface
+      which acts as abstraction layer to the algorithm
+      for MTK platform.
+
+config TAS25XX_ALGO
+    bool "TAS25XX AP DSP Communication Support"
+    depends on SND_SOC_TAS25XX
+    help
+      Enable support for TAS25XX Calibration driver.
+
+      This includes TAS25XX Calibration driver interfaces
+      and functions also interfacing driver to corresponding
+      Platform/DSP
+
+config PLATFORM_QCOM
+    bool "QCOM platform support"
+    depends on TAS25XX_ALGO
+    default n
+    help
+      Enable support for Qcom platform.
+
+      This enables the platform specific interface
+      which acts as abstraction layer to the algorithm
+      for Exynos platform.
+
+config TISA_KBIN_INTF
+    bool "Kbin file method support"
+    depends on PLATFORM_QCOM
+    default n
+    help
+      Enable support for KBin file method
+
+      This is the algorithm specific configuration
+      where the binary file will be opened in the kernel
+      using request_firmware API. This interface currently supported
+      only on the Qualcomm platform
+
+config TAS25XX_CALIB_VAL_BIG
+    bool "For bigdata & calibration support"
+    depends on TAS25XX_ALGO
+    default n
+    help
+      Enable support for bigdata & calibration.
+
+      Enables the support for sysfs entries under
+      /sys/class/tas25xx directory
+      for calibration, validataion and bigdata
+
+config TAS25XX_IRQ_BD
+    bool "For bigdata IRQ data"
+    depends on SND_SOC_TAS25XX
+    default n
+    help
+      Enable support for bigdata & calibration.
+
+      Enables the support for sysfs entries under
+      /sys/class/tas25xx_dev/ directory
+      for irq related big data
+
+config TISA_SYSFS_INTF
+    bool "sysfs interface for calibration and algo support"
+    depends on TAS25XX_ALGO
+    default n
+    help
+      Enable the support for sysfs based interfaces
+      for calibration and algo control
+
+config TAS25XX_MISC
+    bool "Misc Driver support"
+    default y
+    help
+      Enable misc driver support.
+
+      Enable the misc driver "TAS25XX" which is
+      interface to communicate to device via register
+      read and write
+
+endif # SND_SOC_TAS25XX

+ 53 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/Makefile

@@ -0,0 +1,53 @@
+ccflags-y += -I$(srctree)/$(src)/inc/
+ccflags-y += -I$(srctree)/sound/soc/codecs/uda20/driver/tas25xx_driver/inc/
+ccflags-y += -I$(srctree)/sound/soc/mediatek/common/
+ccflags-y += -I$(src)
+
+obj-$(CONFIG_SND_SOC_TAS25XX) += snd-soc-tas25xx.o
+
+snd-soc-tas25xx-$(CONFIG_SND_SOC_TAS25XX) := src/tas25xx-codec.o \
+	src/tas25xx-regmap.o \
+	src/tas25xx.o \
+	src/tas25xx-logic.o \
+	src/tas25xx-regbin-parser.o
+
+ifneq ($(CONFIG_TAS25XX_ALGO),)
+snd-soc-tas25xx-objs += algo/src/tas25xx-algo-intf.o \
+	algo/src/tas25xx-calib.o \
+	algo/src/tas25xx-algo-common.o
+endif
+
+ifneq ($(CONFIG_PLATFORM_MTK),)
+snd-soc-tas25xx-objs += algo/platform/mtk/tas25xx-mtk-wrapper.o
+endif
+
+ifneq ($(CONFIG_TAS25XX_MISC),)
+snd-soc-tas25xx-objs += src/tas25xx-misc.o
+endif
+
+ifneq ($(CONFIG_PLATFORM_QCOM),)
+snd-soc-tas25xx-objs += algo/platform/qcom/tas25xx-algo-qdsp-intf.o
+endif
+
+ifneq ($(CONFIG_TISA_KBIN_INTF),)
+snd-soc-tas25xx-objs += algo/src/tas25xx-algo-kbin-intf.o \
+	algo/src/tas25xx-algo-bin-utils.o
+endif
+
+ifneq ($(CONFIG_TAS25XX_CALIB_VAL_BIG),)
+snd-soc-tas25xx-objs += algo/src/tas25xx-calib-validation.o
+endif
+
+ifneq ($(CONFIG_TISA_SYSFS_INTF),)
+snd-soc-tas25xx-objs += algo/src/tas25xx-sysfs-debugfs-utils.o \
+	algo/src/smartpa-debug-common.o
+endif
+
+ifneq ($(CONFIG_TISA_BIN_INTF),)
+snd-soc-tas25xx-objs += algo/src/tas25xx-algo-bin-intf.o
+endif
+
+ifneq ($(CONFIG_PLATFORM_EXYNOS),)
+snd-soc-tas25xx-objs += algo/platform/exynos/tas25xx-algo-exynos-dsp-intf.o
+snd-soc-tas25xx-objs += algo/platform/exynos/skinprot-sysfs-cb.o
+endif

+ 30 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-codec.h

@@ -0,0 +1,30 @@
+/*
+ * =============================================================================
+ * Copyright (c) 2016  Texas Instruments Inc.
+ *
+ * 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 the Free Software
+ * Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details.
+ *
+ * File:
+ *     tas25xx-codec.h
+ *
+ * Description:
+ *     header file for tas25xx-codec.c
+ *
+ * =============================================================================
+ */
+
+#ifndef _TAS25XX_CODEC_H
+#define _TAS25XX_CODEC_H
+
+#include "tas25xx.h"
+
+int tas25xx_register_codec(struct tas25xx_priv *p_tas25xx);
+int tas25xx_deregister_codec(struct tas25xx_priv *p_tas25xx);
+
+#endif /* _TAS25XX_CODEC_H */

+ 22 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-device.h

@@ -0,0 +1,22 @@
+#ifndef __TAS25XX_DEVICE_
+#define __TAS25XX_DEVICE_
+
+#include "tas25xx.h"
+
+int tas25xx_software_reset(struct tas25xx_priv *p_tas25xx, int ch);
+int tas25xx_set_power_mute(struct tas25xx_priv *p_tas25xx, int ch);
+int tas25xx_tx_set_edge(struct tas25xx_priv *p_tas25xx,
+	unsigned int tx_edge, int ch);
+int tas25xx_tx_set_start_slot(struct tas25xx_priv *p_tas25xx,
+	unsigned int tx_start_slot, int ch);
+int tas25xx_rx_set_edge(struct tas25xx_priv *p_tas25xx,
+	unsigned int rx_edge, int ch);
+int tas25xx_rx_set_bitwidth(struct tas25xx_priv *p_tas25xx,
+	int bitwidth, int ch);
+/* Interrupt Related Functions */
+int tas_dev_interrupt_clear(struct tas25xx_priv *p_tas25xx, int ch);
+int tas_dev_interrupt_enable(struct tas25xx_priv *p_tas25xx, int ch);
+int tas_dev_interrupt_disable(struct tas25xx_priv *p_tas25xx, int ch);
+int tas_dev_interrupt_read(struct tas25xx_priv *p_tas25xx, int ch, int *type);
+
+#endif /* __TAS25XX_DEVICE_ */

+ 43 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-ext.h

@@ -0,0 +1,43 @@
+/*
+ * ALSA SoC Texas Instruments TAS25XX High Performance Smart Amplifier
+ *
+ * Copyright (C) 2024 Texas Instruments, Inc.
+ *
+ * Author: Niranjan H Y
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+#ifndef __TAS25XX_EXT_H__
+#define __TAS25XX_EXT_H__
+
+enum tas_amp_status_t {
+	TAS_AMP_ERR_EINVAL = -3,  /* invalid parameter */
+	TAS_AMP_ERR_FW_LOAD = -2, /* amp fw download failure */
+	TAS_AMP_ERR_I2C	= -1,     /* amp i2c error */
+	TAS_AMP_STATE_UNINIT = 0, /* uninitialized state */
+	TAS_AMP_STATE_BOOT_SUCCESS = 1, /* amp first i2c successful */
+	TAS_AMP_STATE_FW_LOAD_SUCCESS = 2, /* amp fully initialized for playback */
+};
+
+enum tas_amp_error_t {
+	TAS_AMP_NO_ERR = 0,
+	TAS_AMP_SHORTCIRCUIT_ERR = 1,
+	TAS_AMP_ERR_MAX,
+};
+
+/* get the current amp status */
+enum tas_amp_status_t tas25xx_get_state(uint32_t id);
+
+/* error callback for i2c error */
+void tas25xx_register_i2c_error_callback(void (*i2c_err_cb)(uint32_t i2caddr));
+void tas25xx_register_amp_error_callback(void (*amp_err_cb)(int32_t i2caddr, int32_t err));
+
+
+#endif /* __TAS25XX_EXT_H__ */

+ 42 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-logic.h

@@ -0,0 +1,42 @@
+#ifndef __TAS25XX_LOGIC_
+#define __TAS25XX_LOGIC_
+
+#include "tas25xx.h"
+
+#define TAS25XX_STREAM_PLAYBACK 0
+#define TAS25XX_STREAM_CAPTURE 1
+
+int tas25xx_register_device(struct tas25xx_priv  *p_tas25xx);
+int tas25xx_probe(struct tas25xx_priv *p_tas25xx);
+void tas25xx_remove(struct tas25xx_priv *p_tas25xx);
+int tas25xx_load_init(struct tas25xx_priv *p_tas25xx, int chn);
+int tas25xx_irq_work_func(struct tas25xx_priv *p_tas25xx);
+void tas25xx_load_config(struct tas25xx_priv *p_tas25xx, int chn);
+int tas25xx_init_work_func(struct tas25xx_priv *p_tas25xx,
+	struct tas_device *dev_tas25xx);
+int tas25xx_dc_work_func(struct tas25xx_priv *p_tas25xx, int chn);
+void tas_reload(struct tas25xx_priv *p_tas25xx, int chn);
+int tas25xx_set_power_state(struct tas25xx_priv *p_tas25xx,
+		enum tas_power_states_t state, uint32_t chbitmask);
+int tas25xx_iv_vbat_slot_config(struct tas25xx_priv *p_tas25xx,
+	int mn_slot_width);
+int tas25xx_set_bitwidth(struct tas25xx_priv *p_tas25xx,
+	int bitwidth, int stream);
+int tas25xx_set_dai_fmt_for_fmt(struct tas25xx_priv *p_tas25xx,
+	unsigned int fmt);
+int tas25xx_set_tdm_rx_slot(struct tas25xx_priv *p_tas25xx,
+	int slots, int slot_width);
+int tas25xx_set_tdm_tx_slot(struct tas25xx_priv *p_tas25xx,
+	int slots, int slot_width);
+
+int tas25xx_change_book(struct tas25xx_priv *p_tas25xx,
+	int32_t chn, int book);
+
+#if IS_ENABLED(CONFIG_TAS25XX_IRQ_BD)
+void tas25xx_log_interrupt_stats(struct tas25xx_priv *p_tas25xx);
+void tas25xx_clear_interrupt_stats(struct tas25xx_priv *p_tas25xx);
+#endif /* CONFIG_TAS25XX_IRQ_BD */
+
+int tas25xx_get_drv_channel_opmode(void);
+
+#endif /*__TAS25XX_LOGIC_*/

+ 38 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-misc.h

@@ -0,0 +1,38 @@
+/*
+ * =============================================================================
+ * Copyright (c) 2016  Texas Instruments Inc.
+ *
+ * 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 the Free Software
+ * Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * File:
+ *     tas2562-misc.h
+ *
+ * Description:
+ *     header file for tas2562-misc.c
+ *
+ * =============================================================================
+ */
+
+#ifndef _TAS25XX_MISC_H
+#define _TAS25XX_MISC_H
+
+#define	TIAUDIO_CMD_REG_WITE			1
+#define	TIAUDIO_CMD_REG_READ			2
+#define	TIAUDIO_CMD_DEBUG_ON			3
+#define	TIAUDIO_CMD_CALIBRATION			7
+#define	TIAUDIO_CMD_SAMPLERATE			8
+#define	TIAUDIO_CMD_BITRATE				9
+#define	TIAUDIO_CMD_DACVOLUME			10
+#define	TIAUDIO_CMD_SPEAKER				11
+
+int tas25xx_register_misc(struct tas25xx_priv *p_tas25xx);
+int tas25xx_deregister_misc(struct tas25xx_priv *p_tas25xx);
+
+#endif /* _TAS25XX_MISC_H */

+ 120 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-regbin-parser.h

@@ -0,0 +1,120 @@
+/*
+ * ALSA SoC Texas Instruments TAS25XX High Performance 4W Smart Amplifier
+ *
+ * Copyright (C) 2021 Texas Instruments, Inc.
+ *
+ * Author: Vijeth P O
+ * Modified: 10-07-21
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef __TAS25XX_REGBIN_PARSER__
+#define __TAS25XX_REGBIN_PARSER__
+
+#define SampleRate_48000	0
+#define SampleRate_44100	1
+#define SampleRate_96000	2
+
+#define FMT_INV_NB_NF	0
+#define FMT_INV_IB_NF	1
+#define FMT_INV_NB_IF	2
+#define FMT_INV_IB_IF	3
+
+#define FMT_MASK_I2S	0
+#define FMT_MASK_DSP_A	1
+#define FMT_MASK_DSP_B	2
+#define FMT_MASK_LEFT_J	3
+
+#define RX_SLOTS_16	0
+#define RX_SLOTS_24	1
+#define RX_SLOTS_32	2
+
+#define TX_SLOTS_16	0
+#define TX_SLOTS_24	1
+#define TX_SLOTS_32	2
+
+#define RX_BITWIDTH_16	0
+#define RX_BITWIDTH_24	1
+#define RX_BITWIDTH_32	2
+
+#define RX_SLOTLEN_16	0
+#define RX_SLOTLEN_24	1
+#define RX_SLOTLEN_32	2
+
+#define TX_SLOTLEN_16	0
+#define TX_SLOTLEN_24	1
+#define TX_SLOTLEN_32	2
+
+#define SUPPORTED_BIN_VERSION 7
+
+#define TAS25XX_DEFAULT	0xFFFFFFFF
+
+enum kcntl_during_t {
+	KCNTR_ANYTIME = 0, /* instant update */
+	KCNTR_PRE_POWERUP = 1, /* during pre-power up */
+	KCNTR_POST_POWERUP = 2, /* during post-power up */
+};
+
+struct default_hw_params {
+	char sample_rate;
+	char fmt_inv;
+	char fmt_mask;
+	char rx_slots;
+	char tx_slots;
+	char rx_bitwidth;
+	char rx_slotlen;
+	char tx_slotlen;
+};
+
+struct tas_dev {
+	u8 device;
+	u8 i2c_addr;
+};
+
+struct bin_header {
+	u32 version;
+	u8 name[64];
+	u32 timestamp;
+	u32 size;
+	u32 channels;
+	struct tas_dev dev[MAX_CHANNELS];
+	u8 iv_width;
+	u8 vbat_mon;
+	u32 features;
+} __attribute__((packed));
+
+int32_t tas25xx_load_firmware(struct tas25xx_priv *p_tas25xx, int max_retry_count);
+int32_t tas25xx_create_kcontrols(struct tas25xx_priv *p_tas25xx);
+int32_t tas25xx_remove_binfile(struct tas25xx_priv *p_tas25xx);
+int32_t tas25xx_set_init_params(struct tas25xx_priv *p_tas25xx, int32_t ch);
+int32_t tas25xx_set_sample_rate(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t sample_rate);
+int32_t tas25xx_set_fmt_inv(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t fmt_inv);
+int32_t tas25xx_set_fmt_mask(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t fmt_mask);
+int32_t tas25xx_set_rx_slots(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t rx_slot);
+int32_t tas25xx_set_tx_slots(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t tx_slot);
+int32_t tas25xx_set_rx_bitwidth(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t rx_bitwidth);
+int32_t tas25xx_set_rx_slotlen(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t rx_slotlen);
+int32_t tas25xx_set_tx_slotlen(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t tx_slotlen);
+int32_t tas25xx_set_pre_powerup(struct tas25xx_priv *p_tas25xx, int32_t ch);
+int32_t tas25xx_set_post_powerup(struct tas25xx_priv *p_tas25xx, int32_t ch);
+int32_t tas25xx_set_pre_powerdown(struct tas25xx_priv *p_tas25xx, int32_t ch);
+int32_t tas25xx_set_post_powerdown(struct tas25xx_priv *p_tas25xx, int32_t ch);
+
+int32_t tas25xx_process_block(struct tas25xx_priv *p_tas25xx, char *mem, int32_t chn);
+
+int32_t tas25xx_check_if_powered_on(struct tas25xx_priv *p_tas25xx, int *state, int ch);
+int tas_write_init_config_params(struct tas25xx_priv *p_tas25xx, int number_of_channels);
+
+int32_t tas25xx_update_kcontrol_data(struct tas25xx_priv *p_tas25xx, enum kcntl_during_t cur_state,
+	uint32_t chmask);
+
+void tas25xx_prep_dev_for_calib(int start);
+#endif /* __TAS25XX_REGBIN_PARSER__ */

+ 26 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx-regmap.h

@@ -0,0 +1,26 @@
+#ifndef __TAS25XX_REGMAP__
+#define __TAS25XX_REGMAP__
+#include <linux/version.h>
+#include "tas25xx.h"
+
+struct linux_platform {
+	struct device *dev;
+	struct i2c_client *client;
+	struct regmap *regmap[MAX_CHANNELS];
+	struct hrtimer mtimer;
+	struct snd_soc_component *codec;
+	/* device is working, but system is suspended */
+	int (*runtime_suspend)(struct tas25xx_priv *p_tas25xx);
+	int (*runtime_resume)(struct tas25xx_priv *p_tas25xx);
+	bool mb_runtime_suspend;
+	bool i2c_suspend;
+};
+
+void tas25xx_select_cfg_blk(void *pContext, int conf_no,
+	unsigned char block_type);
+void tas25xx_dump_regs(struct tas25xx_priv  *p_tas25xx, int chn);
+
+
+void tas25xx_register_i2c_error_callback(void (*i2c_err_cb)(uint32_t));
+int tas25xx_check_last_i2c_error_n_reset(void);
+#endif /*__TAS25XX_REGMAP__*/

+ 420 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/inc/tas25xx.h

@@ -0,0 +1,420 @@
+/*
+ * ALSA SoC Texas Instruments TAS25XX High Performance 4W Smart Amplifier
+ *
+ * Copyright (C) 2022 Texas Instruments, Inc.
+ *
+ * Author: Niranjan H Y, Vijeth P O
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+#ifndef __TAS25XX__
+#define __TAS25XX__
+
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/hrtimer.h>
+#include <linux/printk.h>
+#include <linux/delay.h>
+#include <sound/soc.h>
+#include <linux/version.h>
+
+#define TAS25XX_DRIVER_TAG  "UDA_0.0.21_6_K6.1"
+
+#define MAX_CHANNELS	4
+
+/* Page Control Register */
+#define TAS25XX_PAGECTL_REG  0
+
+/* Book Control Register (available in page0 of each book) */
+#define TAS25XX_BOOKCTL_PAGE  0
+#define TAS25XX_BOOKCTL_REG  127
+
+#define TAS25XX_REG(book, page, reg)  (((book * 256 * 128) + \
+					(page * 128)) + reg)
+
+#define TAS25XX_BOOK_ID(reg)  (reg / (256 * 128))
+#define TAS25XX_PAGE_ID(reg)  ((reg % (256 * 128)) / 128)
+#define TAS25XX_BOOK_REG(reg)  (reg % (256 * 128))
+#define TAS25XX_PAGE_REG(reg)  ((reg % (256 * 128)) % 128)
+#define TAS25XX_REG_NO_BOOK(reg) (reg - (TAS25XX_BOOK_ID(reg) * 256 * 128))
+
+/* Book */
+#define TAS25XX_BOOK  TAS25XX_REG(0x0, 0x0, 0x7F)
+#define TAS25XX_BOOK_BOOK70_MASK  (0xff << 0)
+
+/* Rev Info */
+#define TAS25XX_REVID_REG TAS25XX_REG(0x0, 0x0, 0x78)
+
+/* data format */
+#define TAS25XX_DATAFORMAT_SHIFT  2
+#define TAS25XX_DATAFORMAT_I2S  0x0
+#define TAS25XX_DATAFORMAT_DSP  0x1
+#define TAS25XX_DATAFORMAT_RIGHT_J  0x2
+#define TAS25XX_DATAFORMAT_LEFT_J  0x3
+
+#define TAS25XX_DAI_FMT_MASK  (0x7 << TAS25XX_DATAFORMAT_SHIFT)
+
+
+#define ERROR_NONE  0x0000000
+#define ERROR_PLL_ABSENT  0x0000001
+#define ERROR_DEVA_I2C_COMM  0x0000002
+#define ERROR_DEVB_I2C_COMM  0x0000004
+#define ERROR_CLOCK  0x0000008
+#define ERROR_YRAM_CRCCHK  0x0000010
+#define ERROR_OVER_CURRENT  0x0000020
+#define ERROR_DIE_OVERTEMP  0x0000040
+#define ERROR_OVER_VOLTAGE  0x0000080
+#define ERROR_UNDER_VOLTAGE  0x0000100
+#define ERROR_BROWNOUT  0x0000200
+#define ERROR_CLASSD_PWR  0x0000400
+#define ERROR_FAILSAFE  0x0000800
+
+#define TAS25XX_IRQ_DET_TIMEOUT		30000
+#define TAS25XX_IRQ_DET_CNT_LIMIT	500
+
+/* 5 second */
+#define CHECK_PERIOD  5000
+
+#define TAS25XX_I2C_RETRY_COUNT  3
+
+#define TAS25XX_SWITCH  0x10000001
+#define TAS25XX_RIGHT_SWITCH  0x10000002
+#define RX_SCFG_LEFT  0x10000003
+#define RX_SCFG_RIGHT  0x10000004
+
+#define RESTART_MAX  3
+#define MAX_CMD_LIST 5 /* sysfs cmd nodes */
+
+struct tas25xx_priv;
+struct snd_soc_component;
+
+/* REGBIN related */
+#define TAS25XX_CONFIG_SIZE  (10)
+#define TAS25XX_DEVICE_SUM  (8)
+
+#define TAS25XX_CMD_SING_W  (0x1)
+#define TAS25XX_CMD_BURST  (0x2)
+#define TAS25XX_CMD_DELAY  (0x3)
+#define TAS25XX_CMD_FIELD_W  (0x4)
+
+#define SMS_HTONS(a, b)  ((((a)&0x00FF)<<8) | \
+				((b)&0x00FF))
+#define SMS_HTONL(a, b, c, d) ((((a)&0x000000FF)<<24) |\
+					(((b)&0x000000FF)<<16) | \
+					(((c)&0x000000FF)<<8) | \
+					((d)&0x000000FF))
+
+
+#define CMD_SINGLE_WRITE	0
+#define CMD_BURST_WRITES	1
+#define CMD_UPDATE_BITS		2
+#define CMD_DELAY		3
+
+#define CMD_SINGLE_WRITE_SZ 6
+#define CMD_BURST_WRITES_SZ 9
+#define CMD_UPDATE_BITS_SZ  7
+#define CMD_DELAY_SZ		5
+
+#define DSP_FW_LOAD_NTRIES  20
+
+#define INTERRUPT_TYPE_CLOCK_BASED (1<<0)
+#define INTERRUPT_TYPE_NON_CLOCK_BASED (1<<1)
+
+enum tas_power_states_t {
+	TAS_POWER_ACTIVE = 0,
+	TAS_POWER_MUTE = 1,
+	TAS_POWER_SHUTDOWN = 2,
+};
+
+enum tas25xx_dsp_fw_state {
+	TAS25XX_DSP_FW_NONE = 0,
+	TAS25XX_DSP_FW_TRYLOAD,
+	TAS25XX_DSP_FW_PARSE_FAIL,
+	TAS25XX_DSP_FW_LOAD_FAIL,
+	TAS25XX_DSP_FW_OK,
+};
+
+enum tas25xx_bin_blk_type {
+	TAS25XX_BIN_BLK_COEFF = 1,
+	TAS25XX_BIN_BLK_POST_POWER_UP,
+	TAS25XX_BIN_BLK_PRE_SHUTDOWN,
+	TAS25XX_BIN_BLK_PRE_POWER_UP,
+	TAS25XX_BIN_BLK_POST_SHUTDOWN
+};
+
+enum tas_int_action_t {
+	TAS_INT_ACTION_NOACTION = 0,
+	TAS_INT_ACTION_SW_RESET = 1 << 0,
+	TAS_INT_ACTION_HW_RESET = 1 << 1,
+	TAS_INT_ACTION_POWER_ON = 1 << 2,
+};
+
+struct tas25xx_block_data {
+	unsigned char dev_idx;
+	unsigned char block_type;
+	unsigned short yram_checksum;
+	unsigned int block_size;
+	unsigned int nSublocks;
+	unsigned char *regdata;
+};
+
+struct tas25xx_config_info {
+	unsigned int nblocks;
+	unsigned int real_nblocks;
+	struct tas25xx_block_data **blk_data;
+};
+
+void tas25xx_select_cfg_blk(void *pContext, int conf_no,
+	unsigned char block_type);
+int tas25xx_load_container(struct tas25xx_priv *pTAS256x);
+void tas25xx_config_info_remove(void *pContext);
+
+struct tas25xx_register {
+	int book;
+	int page;
+	int reg;
+};
+
+/* Used for Register Dump */
+struct tas25xx_reg {
+	unsigned int reg_index;
+	unsigned int reg_val;
+};
+
+struct tas25xx_dai_cfg {
+unsigned int dai_fmt;
+unsigned int tdm_delay;
+};
+
+/*struct tas25xx_buf_cfg {
+ *	unsigned short bufSize;
+ *	unsigned char *buf;
+ *};
+ */
+
+//This should be removed or modified
+enum ch_bitmask {
+	channel_left = 0x1 << 0,
+	channel_right = 0x1 << 1,
+	channel_both = channel_left|channel_right
+};
+
+/*
+ * device ops function structure
+ */
+struct tas_device_ops {
+	/**< init typically for loading optimal settings */
+	int (*tas_init)(struct tas25xx_priv *p_tas25xx, int chn);
+	int (*tas_probe)(struct tas25xx_priv *p_tas25xx,
+		struct snd_soc_component *codec, int chn);
+/*TODO:*/
+};
+
+
+struct tas_device {
+	int mn_chip_id;
+	int mn_current_book;
+	int mn_addr;
+	int reset_gpio;
+	int irq_gpio;
+	int irq_no;
+	int device_id;
+	int channel_no;
+	int rx_mode;
+	int dvc_pcm;
+	int bst_vltg;
+	int bst_ilm;
+	int ampoutput_lvl;
+	int lim_switch;
+	int lim_max_attn;
+	int lim_thr_max;
+	int lim_thr_min;
+	int lim_att_rate;
+	int lim_rel_rate;
+	int lim_att_stp_size;
+	int lim_rel_stp_size;
+	int lim_max_thd;
+	int lim_min_thd;
+	int lim_infl_pt;
+	int lim_trk_slp;
+	int bop_enable;
+	int bop_thd;
+	int bop_att_rate;
+	int bop_att_stp_size;
+	int bop_hld_time;
+	int bop_mute;
+	int bosd_enable;
+	int bosd_thd;
+	int vbat_lpf;
+	int rx_cfg;
+	int classh_timer;
+	int reciever_enable;
+	int icn_sw;
+	int icn_thr;
+	int icn_hyst;
+	struct tas_device_ops dev_ops;
+	struct delayed_work init_work;
+	struct tas25xx_priv *prv_data;
+	/* interrupt count since interrupt enable */
+	uint8_t irq_count;
+	unsigned long jiffies;
+	/* Fail Safe */
+	unsigned int mn_restart;
+};
+
+struct tas25xx_intr_info {
+	char name[64];
+	int32_t reg;
+	int32_t mask;
+	int32_t action;
+	int32_t detected;
+	int32_t is_clock_based;
+	int32_t notify_int_val;
+	uint32_t count;
+	uint64_t count_persist;
+	struct device_attribute *dev_attr;
+};
+
+struct tas25xx_interrupts {
+	uint8_t count;
+	uint8_t *buf_intr_enable;
+	uint8_t *buf_intr_disable;
+	uint8_t *buf_intr_clear;
+	struct tas25xx_intr_info *intr_info;
+	uint32_t processing_delay;
+};
+
+struct tas_regbin_read_blok_t {
+	int32_t reg;
+	int32_t mask;
+	int32_t value;
+};
+
+struct tas_block_op_data_t {
+	uint32_t no_of_rx_blks;
+	uint32_t no_of_tx_blks;
+	uint8_t *sw_reset;
+	uint8_t *power_check;
+	uint8_t *mute;
+	uint8_t *cal_init;
+	uint8_t *cal_deinit;
+	uint8_t *rx_fmt_data;
+	uint8_t *tx_fmt_data;
+};
+
+#if IS_ENABLED(CONFIG_TAS25XX_IRQ_BD)
+struct irq_bigdata {
+	struct device_attribute *p_dev_attr;
+	struct attribute **p_attr_arr;
+	struct device *irq_dev;
+};
+#endif
+
+struct cmd_data {
+	struct device_attribute *p_dev_attr;
+	struct attribute **p_attr_arr;
+	struct device *cmd_dev;
+};
+
+struct tas25xx_priv {
+	struct linux_platform *platform_data;
+	struct kobject *k_obj;
+	int m_power_state;
+	int mn_frame_size;
+	int mn_ppg;
+	int mn_ch_size;
+	int mn_rx_width;
+	int mn_tx_slot_width;
+	int sample_rate;
+	int mn_iv_width;
+	int curr_mn_iv_width;
+	int mn_vbat;
+	int curr_mn_vbat;
+	int ch_count;
+	int mn_slots;
+	unsigned int mn_fmt;
+	int mn_fmt_mode;
+	int mn_frame_start;
+	int mn_rx_edge;
+	int mn_rx_offset;
+	int mn_tx_edge;
+	int mn_tx_offset;
+	int *ti_amp_state;
+	int dac_power;	/* this is set based on the DAC events */
+	struct tas_device **devs;
+	int (*read)(struct tas25xx_priv *p_tas25xx, int32_t chn,
+		unsigned int reg, unsigned int *pValue);
+	int (*write)(struct tas25xx_priv *p_tas25xx, int32_t chn,
+		unsigned int reg, unsigned int Value);
+	int (*bulk_read)(struct tas25xx_priv *p_tas25xx, int32_t chn,
+		unsigned int reg, unsigned char *p_data, unsigned int len);
+	int (*bulk_write)(struct tas25xx_priv *p_tas25xx, int32_t chn,
+		unsigned int reg, unsigned char *p_data, unsigned int len);
+	int (*update_bits)(struct tas25xx_priv *p_tas25xx, int32_t chn,
+		unsigned int reg, unsigned int mask, unsigned int value);
+	void (*hw_reset)(struct tas25xx_priv *p_tas25xx);
+	void (*clear_irq)(struct tas25xx_priv *p_tas25xx);
+	void (*enable_irq)(struct tas25xx_priv *p_tas25xx);
+	void (*disable_irq)(struct tas25xx_priv *p_tas25xx);
+	unsigned int mn_err_code;
+	int (*plat_write)(void *plat_data,
+		unsigned int i2c_addr, unsigned int reg, unsigned int value,
+		unsigned int channel);
+	int (*plat_bulk_write)(void *plat_data, unsigned int i2c_addr,
+		unsigned int reg, unsigned char *pData,
+		unsigned int nLength, unsigned int channel);
+	int (*plat_read)(void *plat_data, unsigned int i2c_addr,
+		unsigned int reg, unsigned int *value, unsigned int channel);
+	int (*plat_bulk_read)(void *plat_data, unsigned int i2c_addr,
+		unsigned int reg, unsigned char *pData,
+		unsigned int nLength, unsigned int channel);
+	int (*plat_update_bits)(void *plat_data, unsigned int i2c_addr,
+		unsigned int reg, unsigned int mask,
+		unsigned int value, unsigned int channel);
+	void (*schedule_init_work)(struct tas25xx_priv *p_tas25xx, int ch);
+	void (*cancel_init_work) (struct tas25xx_priv *p_tas25xx, int ch);
+	struct mutex dev_lock;
+	struct mutex codec_lock;
+	struct mutex file_lock;
+	struct delayed_work irq_work;
+	struct delayed_work dc_work;
+	int iv_enable;
+	uint32_t dev_revid;
+	uint32_t fw_size;
+	uint8_t *fw_data;
+	struct delayed_work post_fw_load_work;
+	struct delayed_work fw_load_work;
+	wait_queue_head_t fw_wait;
+	int fw_load_retry_count;
+	atomic_t fw_state;
+	atomic_t fw_wait_complete;
+	wait_queue_head_t dev_init_wait;
+	atomic_t dev_init_status;
+	int device_used;
+	int irq_enabled[MAX_CHANNELS];
+	struct tas25xx_interrupts intr_data[MAX_CHANNELS];
+#if IS_ENABLED(CONFIG_TAS25XX_IRQ_BD)
+	struct irq_bigdata irqdata;
+#endif
+	struct class *class;
+	struct cmd_data cmd_data;
+	struct tas_block_op_data_t block_op_data[MAX_CHANNELS];
+};
+
+static inline int is_power_up_state(enum tas_power_states_t state)
+{
+	if (state == TAS_POWER_ACTIVE)
+		return 1;
+
+	return 0;
+}
+
+#endif /* __TAS25XX__ */
+

+ 716 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/src/tas25xx-codec.c

@@ -0,0 +1,716 @@
+/*
+ * =============================================================================
+ * Copyright (c) 2016  Texas Instruments Inc.
+ *
+ * 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 the Free Software
+ * Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details.
+ *
+ * File:
+ *     tas25xx-codec.c
+ *
+ * Description:
+ *     ALSA SoC driver for Texas Instruments TAS25XX High Performance 4W Smart
+ *     Amplifier
+ *
+ * =============================================================================
+ */
+
+#define DEBUG 5
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/version.h>
+
+#include "../inc/tas25xx.h"
+#include "../inc/tas25xx-ext.h"
+#include "../inc/tas25xx-device.h"
+#include "../inc/tas25xx-logic.h"
+#include "../inc/tas25xx-regmap.h"
+#include "../inc/tas25xx-regbin-parser.h"
+#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
+#include "../algo/inc/tas_smart_amp_v2.h"
+#include "../algo/inc/tas25xx-calib.h"
+#endif /*CONFIG_TAS25XX_ALGO*/
+
+static const char *irq_gpio_label[2] = {
+	"TAS25XX-IRQ", "TAS25XX-IRQ2"
+};
+
+static struct tas25xx_priv *s_tas25xx;
+
+int tas25xx_start_fw_load(struct tas25xx_priv *p_tas25xx, int retry_count);
+
+static unsigned int tas25xx_codec_read(struct snd_soc_component *codec,
+		unsigned int reg)
+{
+	unsigned int value = 0;
+	struct tas25xx_priv *p_tas25xx = snd_soc_component_get_drvdata(codec);
+	int ret = -1;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	switch (reg) {
+	case TAS25XX_SWITCH:
+		dev_dbg(plat_data->dev, "%s: %d, %d TAS25XX_SWITCH",
+			__func__, reg, value);
+		value = p_tas25xx->device_used;
+		break;
+
+	default:
+		dev_dbg(plat_data->dev, "%s: %d, %d default read",
+		__func__, reg, value);
+		ret = p_tas25xx->read(p_tas25xx, 0, reg, &value);
+		break;
+	}
+
+	dev_dbg(plat_data->dev, "%s, reg=%d, value=%d", __func__, reg, value);
+
+	if (ret == 0)
+		return value;
+	else
+		return ret;
+}
+
+
+static int tas25xx_codec_write(struct snd_soc_component *codec,
+				unsigned int reg, unsigned int value)
+{
+	struct tas25xx_priv *p_tas25xx = snd_soc_component_get_drvdata(codec);
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+	int ret = 0;
+
+	switch (reg) {
+	case TAS25XX_SWITCH:
+		dev_dbg(plat_data->dev, "%s: %d, %d TAS25XX_SWITCH",
+			__func__, reg, value);
+		p_tas25xx->device_used = value;
+		break;
+
+	default:
+		ret = -EINVAL;
+		dev_dbg(plat_data->dev, "%s: %d, %d UNIMPLEMENTED",
+		__func__, reg, value);
+		break;
+	}
+
+	return ret;
+}
+
+
+#if IS_ENABLED(CODEC_PM)
+static int tas25xx_codec_suspend(struct snd_soc_component *codec)
+{
+	struct tas25xx_priv *p_tas25xx = snd_soc_component_get_drvdata(codec);
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+	int ret = -1;
+
+	dev_dbg(plat_data->dev, "%s\n", __func__);
+
+	mutex_lock(&p_tas25xx->codec_lock);
+	ret = plat_data->runtime_suspend(p_tas25xx);
+	mutex_unlock(&p_tas25xx->codec_lock);
+
+	return ret;
+}
+
+static int tas25xx_codec_resume(struct snd_soc_component *codec)
+{
+	struct tas25xx_priv *p_tas25xx = snd_soc_component_get_drvdata(codec);
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+	int ret = 0;
+
+	mutex_lock(&p_tas25xx->codec_lock);
+
+	dev_dbg(plat_data->dev, "%s\n", __func__);
+	ret = plat_data->runtime_resume(p_tas25xx);
+
+	mutex_unlock(&p_tas25xx->codec_lock);
+	return ret;
+}
+
+#endif
+
+static int tas25xx_dac_event(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+	struct tas25xx_priv *p_tas25xx = snd_soc_component_get_drvdata(codec);
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+	int ret = -1;
+
+	mutex_lock(&p_tas25xx->codec_lock);
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		p_tas25xx->dac_power = 1;
+		dev_info(plat_data->dev, "SND_SOC_DAPM_POST_PMU\n");
+		ret = tas25xx_set_power_state(p_tas25xx, TAS_POWER_ACTIVE, 0xffff);
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		p_tas25xx->dac_power = 0;
+		dev_info(plat_data->dev, "SND_SOC_DAPM_PRE_PMD\n");
+		if (p_tas25xx->m_power_state != TAS_POWER_SHUTDOWN)
+			ret = tas25xx_set_power_state(p_tas25xx, TAS_POWER_SHUTDOWN, 0xffff);
+		else
+			ret = 0;
+		break;
+	}
+	mutex_unlock(&p_tas25xx->codec_lock);
+
+	return ret;
+}
+
+static const struct snd_kcontrol_new dapm_switch =
+	SOC_DAPM_SINGLE("Switch", TAS25XX_SWITCH, 0, 1, 0);
+
+static const struct snd_soc_dapm_widget tas25xx_dapm_widgets[] = {
+	SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_SWITCH("TAS25XX ASI", SND_SOC_NOPM, 0, 0, &dapm_switch),
+	SND_SOC_DAPM_AIF_OUT("Voltage Sense", "ASI1 Capture",  1,
+		SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("Current Sense", "ASI1 Capture",  0,
+		SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas25xx_dac_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_OUTPUT("OUT"),
+	SND_SOC_DAPM_SIGGEN("VMON"),
+	SND_SOC_DAPM_SIGGEN("IMON")
+};
+
+static const struct snd_soc_dapm_route tas25xx_audio_map[] = {
+	{"DAC", NULL, "ASI1"},
+	{"TAS25XX ASI", "Switch", "DAC"},
+	{"OUT", NULL, "TAS25XX ASI"},
+	{"Voltage Sense", NULL, "VMON"},
+	{"Current Sense", NULL, "IMON"},
+};
+
+static bool fw_load_required(struct tas25xx_priv *p_tas25xx)
+{
+	return (atomic_read(&p_tas25xx->fw_state) == TAS25XX_DSP_FW_LOAD_FAIL);
+}
+
+static int tas25xx_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	int i;
+	struct snd_soc_component *codec = dai->component;
+	struct tas25xx_priv *p_tas25xx
+			= snd_soc_component_get_drvdata(codec);
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+	int bitwidth = 16;
+	int ret = -EINVAL;
+	unsigned int channels = params_channels(params);
+
+	if (fw_load_required(p_tas25xx)) {
+		dev_warn(plat_data->dev, "%s, firmware is not loaded, retry", __func__);
+		ret = tas25xx_start_fw_load(p_tas25xx, 3);
+		if (ret < 0) {
+			dev_err(plat_data->dev, "%s fw load failed", __func__);
+			return ret;
+		}
+	}
+
+	mutex_lock(&p_tas25xx->codec_lock);
+
+	dev_dbg(plat_data->dev, "%s, stream %s format: %d\n", __func__,
+		(substream->stream ==
+			SNDRV_PCM_STREAM_PLAYBACK) ? ("Playback") : ("Capture"),
+		params_format(params));
+
+	if (channels > 2) {
+		/* assume TDM mode */
+		p_tas25xx->mn_fmt_mode = 2;
+
+		switch (params_format(params)) {
+		case SNDRV_PCM_FORMAT_S16_LE:
+			bitwidth = 16;
+			break;
+		case SNDRV_PCM_FORMAT_S24_LE:
+			bitwidth = 24;
+			break;
+		case SNDRV_PCM_FORMAT_S32_LE:
+			bitwidth = 32;
+			break;
+		}
+
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			ret = tas25xx_set_tdm_rx_slot(p_tas25xx, channels,
+				bitwidth);
+		else /*Assumed Capture*/
+			ret = tas25xx_set_tdm_tx_slot(p_tas25xx, channels,
+				bitwidth);
+	} else {
+		/* assume I2S mode*/
+		p_tas25xx->mn_fmt_mode = 1;
+		switch (params_format(params)) {
+		case SNDRV_PCM_FORMAT_S16_LE:
+			bitwidth = 16;
+			break;
+		case SNDRV_PCM_FORMAT_S24_LE:
+			bitwidth = 24;
+			break;
+		case SNDRV_PCM_FORMAT_S32_LE:
+			bitwidth = 32;
+			break;
+		}
+
+		ret = tas25xx_set_bitwidth(p_tas25xx,
+				bitwidth, substream->stream);
+		if (ret < 0) {
+			dev_info(plat_data->dev, "set bitwidth failed, %d\n",
+				ret);
+			goto ret;
+		}
+	}
+
+	dev_info(plat_data->dev, "%s, stream %s sample rate: %d\n", __func__,
+		(substream->stream ==
+			SNDRV_PCM_STREAM_PLAYBACK) ? ("Playback") : ("Capture"),
+		params_rate(params));
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		for (i = 0; i < p_tas25xx->ch_count; i++)
+			ret = tas25xx_set_sample_rate(p_tas25xx, i, params_rate(params));
+
+ret:
+	mutex_unlock(&p_tas25xx->codec_lock);
+	return ret;
+}
+
+static int tas25xx_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_component *codec = dai->component;
+	struct tas25xx_priv *p_tas25xx = snd_soc_component_get_drvdata(codec);
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+	int ret = -EINVAL;
+
+	if (fw_load_required(p_tas25xx)) {
+		dev_warn(plat_data->dev, "%s, firmware is not loaded, retry", __func__);
+		ret = tas25xx_start_fw_load(p_tas25xx, 3);
+		if (ret < 0) {
+			dev_err(plat_data->dev, "%s fw load failed", __func__);
+			return ret;
+		}
+	}
+
+	dev_info(plat_data->dev, "%s, format=0x%x\n", __func__, fmt);
+
+	p_tas25xx->mn_fmt = fmt;
+	ret = tas25xx_set_dai_fmt_for_fmt(p_tas25xx, fmt);
+
+	return ret;
+}
+
+static int tas25xx_set_dai_tdm_slot(struct snd_soc_dai *dai,
+		unsigned int tx_mask, unsigned int rx_mask,
+		int slots, int slot_width)
+{
+	int ret = -EINVAL;
+	struct snd_soc_component *codec = dai->component;
+	struct tas25xx_priv *p_tas25xx = snd_soc_component_get_drvdata(codec);
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	if (fw_load_required(p_tas25xx)) {
+		dev_warn(plat_data->dev, "%s, firmware is not loaded, retry", __func__);
+		ret = tas25xx_start_fw_load(p_tas25xx, 3);
+		if (ret < 0) {
+			dev_err(plat_data->dev, "%s fw load failed", __func__);
+			return ret;
+		}
+	}
+
+	dev_dbg(plat_data->dev, "%s, tx_mask:%d, rx_mask:%d",
+		__func__, tx_mask, rx_mask);
+	dev_dbg(plat_data->dev, "%s, slots:%d,slot_width:%d",
+		__func__, slots, slot_width);
+
+	if (rx_mask) {
+		p_tas25xx->mn_fmt_mode = 2; /*TDM Mode*/
+		ret = tas25xx_set_tdm_rx_slot(p_tas25xx, slots, slot_width);
+	} else if (tx_mask) {
+		p_tas25xx->mn_fmt_mode = 2;
+		ret = tas25xx_set_tdm_tx_slot(p_tas25xx, slots, slot_width);
+	} else {
+		dev_err(plat_data->dev, "%s, Invalid Mask",
+				__func__);
+		p_tas25xx->mn_fmt_mode = 0;
+	}
+
+	return ret;
+}
+
+static int tas25xx_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+	int ret = 0;
+	struct snd_soc_component *codec = dai->component;
+	struct tas25xx_priv *p_tas25xx = snd_soc_component_get_drvdata(codec);
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	if (fw_load_required(p_tas25xx)) {
+		dev_warn(plat_data->dev, "%s, firmware is not loaded, retry", __func__);
+		ret = tas25xx_start_fw_load(p_tas25xx, 3);
+		if (ret < 0) {
+			dev_err(plat_data->dev, "%s fw load failed", __func__);
+			return ret;
+		}
+	}
+
+	dev_dbg(plat_data->dev, "%s, stream %s mute %d\n", __func__,
+		(stream == SNDRV_PCM_STREAM_PLAYBACK) ? ("Playback") : ("Capture"),
+		mute);
+
+	if (stream != SNDRV_PCM_STREAM_PLAYBACK)
+		return ret;
+
+	mutex_lock(&p_tas25xx->codec_lock);
+	if (mute)
+		ret = tas25xx_set_power_state(p_tas25xx, TAS_POWER_MUTE,
+			(0xf & tas25xx_get_drv_channel_opmode()));
+	mutex_unlock(&p_tas25xx->codec_lock);
+
+	if (mute) {
+#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
+		tas25xx_stop_algo_processing();
+#endif /* CONFIG_TAS25XX_ALGO */
+
+#if IS_ENABLED(CONFIG_TAS25XX_IRQ_BD)
+		tas25xx_log_interrupt_stats(p_tas25xx);
+#endif /* CONFIG_TAS25XX_IRQ_BD */
+	} else {
+#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
+		tas25xx_start_algo_processing(p_tas25xx->curr_mn_iv_width,
+			p_tas25xx->curr_mn_vbat);
+#endif /* CONFIG_TAS25XX_ALGO */
+#if IS_ENABLED(CONFIG_TAS25XX_IRQ_BD)
+		tas25xx_clear_interrupt_stats(p_tas25xx);
+#endif /* CONFIG_TAS25XX_IRQ_BD */
+	}
+
+	return ret;
+}
+
+static struct snd_soc_dai_ops tas25xx_dai_ops = {
+	.hw_params  = tas25xx_hw_params,
+	.set_fmt    = tas25xx_set_dai_fmt,
+	.set_tdm_slot = tas25xx_set_dai_tdm_slot,
+	.mute_stream = tas25xx_mute_stream,
+};
+
+#define TAS25XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+						SNDRV_PCM_FMTBIT_S20_3LE |\
+						SNDRV_PCM_FMTBIT_S24_LE |\
+						SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver tas25xx_dai_driver[] = {
+	{
+		.name = "tas25xx ASI1",
+		.id = 0,
+		.playback = {
+			.stream_name    = "ASI1 Playback",
+			.channels_min   = 1,
+			.channels_max   = 8,
+			.rates      = SNDRV_PCM_RATE_8000_192000,
+			.formats    = TAS25XX_FORMATS,
+		},
+		.capture = {
+			.stream_name    = "ASI1 Capture",
+			.channels_min   = 1,
+			.channels_max   = 8,
+			.rates          = SNDRV_PCM_RATE_8000_192000,
+			.formats    = TAS25XX_FORMATS,
+		},
+		.ops = &tas25xx_dai_ops,
+		.symmetric_rate = 1,
+	},
+};
+
+static irqreturn_t tas25xx_irq_handler(int irq, void *dev_id)
+{
+	struct tas25xx_priv *p_tas25xx = (struct tas25xx_priv *)dev_id;
+
+	if (p_tas25xx != s_tas25xx)
+		return IRQ_NONE;
+
+	schedule_delayed_work(&p_tas25xx->irq_work,
+		msecs_to_jiffies(p_tas25xx->intr_data[0].processing_delay));
+	return IRQ_HANDLED;
+}
+
+static int tas25xx_setup_irq(struct tas25xx_priv *p_tas25xx)
+{
+	int i, ret = -EINVAL;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	if (!plat_data)
+		return ret;
+
+	/* register for interrupts */
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		if (gpio_is_valid(p_tas25xx->devs[i]->irq_gpio)) {
+			ret = gpio_request(p_tas25xx->devs[i]->irq_gpio,
+					irq_gpio_label[i]);
+			if (ret) {
+				dev_err(plat_data->dev,
+					"%s:%u: ch 0x%02x: GPIO %d request error\n",
+					__func__, __LINE__,
+					p_tas25xx->devs[i]->mn_addr,
+					p_tas25xx->devs[i]->irq_gpio);
+				continue;
+			}
+			gpio_direction_input(p_tas25xx->devs[i]->irq_gpio);
+
+			p_tas25xx->devs[i]->irq_no =
+				gpio_to_irq(p_tas25xx->devs[i]->irq_gpio);
+			dev_info(plat_data->dev, "irq = %d\n",
+				p_tas25xx->devs[i]->irq_no);
+
+			ret = devm_request_threaded_irq(plat_data->dev,
+					p_tas25xx->devs[i]->irq_no, tas25xx_irq_handler, NULL,
+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED,
+					"tas25xx", p_tas25xx);
+			if (ret) {
+				dev_err(plat_data->dev, "request_irq failed, error=%d\n", ret);
+			} else {
+				p_tas25xx->irq_enabled[i] = 1;
+				dev_info(plat_data->dev, "Interrupt registration successful!!!");
+			}
+		}
+	}
+
+	return ret;
+}
+
+static int init_dev_with_fw_data(struct tas25xx_priv *p_tas25xx)
+{
+	int ret, i;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	/* software reset and initial writes */
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		ret = tas25xx_software_reset(p_tas25xx, i);
+		if (ret < 0) {
+			dev_err(plat_data->dev, "I2c fail, %d\n", ret);
+			goto post_fw_load_work_done;
+		}
+	}
+
+	ret = tas_write_init_config_params(p_tas25xx, p_tas25xx->ch_count);
+	if (ret) {
+		dev_err(plat_data->dev, "Failed to initialize, error=%d", ret);
+		goto post_fw_load_work_done;
+	}
+
+	ret = tas25xx_probe(p_tas25xx);
+	if (ret) {
+		dev_err(plat_data->dev, "Failed to initialize, error=%d", ret);
+		goto post_fw_load_work_done;
+	}
+
+	ret = tas25xx_setup_irq(p_tas25xx);
+	if (ret) {
+		dev_err(plat_data->dev, "failed to initialize irq=%d", ret);
+	}
+
+post_fw_load_work_done:
+	return ret;
+}
+
+static void fw_load_work_routine(struct work_struct *work)
+{
+	int ret;
+	struct linux_platform *plat_data = NULL;
+	struct tas25xx_priv *p_tas25xx =
+		container_of(work, struct tas25xx_priv, fw_load_work.work);
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	ret = tas25xx_load_firmware(p_tas25xx, p_tas25xx->fw_load_retry_count);
+	dev_info(plat_data->dev, "%s FW loading %s", __func__,
+		!ret ? "success" : "fail");
+
+	if (!ret) {
+		ret = init_dev_with_fw_data(p_tas25xx);
+		if (ret)
+			dev_err(plat_data->dev,
+				"%s fw dnld to device error=%d", __func__, ret);
+		else
+			atomic_set(&p_tas25xx->dev_init_status, 1);
+	}
+
+	if (ret)
+		atomic_set(&p_tas25xx->dev_init_status, ret);
+
+	wake_up(&p_tas25xx->dev_init_wait);
+}
+
+int tas25xx_start_fw_load(struct tas25xx_priv *p_tas25xx, int retry_count)
+{
+	int i, ret, i2c_err, ch_count;
+	struct linux_platform *plat_data = NULL;
+
+	atomic_set(&p_tas25xx->dev_init_status, 0);
+	atomic_set(&p_tas25xx->fw_state, TAS25XX_DSP_FW_TRYLOAD);
+	p_tas25xx->fw_load_retry_count = retry_count;
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+	ch_count = p_tas25xx->ch_count;
+
+	tas25xx_check_last_i2c_error_n_reset();
+	INIT_DELAYED_WORK(&p_tas25xx->fw_load_work, fw_load_work_routine);
+	schedule_delayed_work(&p_tas25xx->fw_load_work, msecs_to_jiffies(0));
+
+	wait_event_interruptible(p_tas25xx->dev_init_wait,
+	atomic_read(&p_tas25xx->dev_init_status) != 0);
+
+	/* set -ve errno or success 1*/
+	ret = atomic_read(&p_tas25xx->dev_init_status);
+	if (ret == 1) {
+		for (i = 0; i < ch_count; i++)
+			p_tas25xx->ti_amp_state[i] = TAS_AMP_STATE_FW_LOAD_SUCCESS;
+	} else {
+		i2c_err = tas25xx_check_last_i2c_error_n_reset();
+		for (i = 0; i < ch_count; i++)
+			if (i2c_err)
+				p_tas25xx->ti_amp_state[i] = TAS_AMP_ERR_I2C;
+			else
+				p_tas25xx->ti_amp_state[i] = TAS_AMP_ERR_FW_LOAD;
+	}
+
+	return ret;
+}
+
+static int tas25xx_codec_probe(struct snd_soc_component *codec)
+{
+	int ret = -1, i = 0;
+	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(codec);
+	struct tas25xx_priv *p_tas25xx = snd_soc_component_get_drvdata(codec);
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+	char *w_name[4] = {NULL};
+	const char *prefix = codec->name_prefix;
+	int w_count = 0;
+
+	if (plat_data)
+		plat_data->codec = codec;
+
+	s_tas25xx = p_tas25xx;
+
+	/*Moved from machine driver to codec*/
+	if (prefix) {
+		w_name[0] = kasprintf(GFP_KERNEL, "%s %s",
+				prefix, "ASI1 Playback");
+		w_name[1] = kasprintf(GFP_KERNEL, "%s %s",
+				prefix, "ASI1 Capture");
+		w_name[2] = kasprintf(GFP_KERNEL, "%s %s",
+			prefix, "OUT");
+		w_count = 3;
+	} else {
+		w_name[0] = kasprintf(GFP_KERNEL, "%s", "ASI1 Playback");
+		w_name[1] = kasprintf(GFP_KERNEL, "%s", "ASI1 Capture");
+		w_name[2] = kasprintf(GFP_KERNEL, "%s", "OUT");
+		w_count = 3;
+	}
+
+	for (i = 0; i < w_count; i++) {
+		snd_soc_dapm_ignore_suspend(dapm, w_name[i]);
+		kfree(w_name[i]);
+	}
+
+	snd_soc_dapm_sync(dapm);
+
+	ret = tas25xx_start_fw_load(p_tas25xx, 800);
+	if (ret == -ENOENT)
+		ret = 0;
+
+	dev_info(plat_data->dev, "%s returning ret=%d\n",
+		__func__, ret);
+
+	return ret;
+}
+
+static void tas25xx_codec_remove(struct snd_soc_component *codec)
+{
+	struct tas25xx_priv *p_tas25xx = snd_soc_component_get_drvdata(codec);
+
+	tas25xx_remove(p_tas25xx);
+	s_tas25xx = NULL;
+}
+
+static struct snd_soc_component_driver soc_codec_driver_tas25xx = {
+	.probe			= tas25xx_codec_probe,
+	.remove			= tas25xx_codec_remove,
+	.read			= tas25xx_codec_read,
+	.write			= tas25xx_codec_write,
+#if IS_ENABLED(CODEC_PM)
+	.suspend		= tas25xx_codec_suspend,
+	.resume			= tas25xx_codec_resume,
+#endif
+	.dapm_widgets		= tas25xx_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(tas25xx_dapm_widgets),
+	.dapm_routes		= tas25xx_audio_map,
+	.num_dapm_routes	= ARRAY_SIZE(tas25xx_audio_map),
+};
+
+
+int tas25xx_register_codec(struct tas25xx_priv *p_tas25xx)
+{
+	int ret = -1;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	dev_info(plat_data->dev, "%s, enter\n", __func__);
+
+	ret = devm_snd_soc_register_component(plat_data->dev,
+		&soc_codec_driver_tas25xx,
+		tas25xx_dai_driver, ARRAY_SIZE(tas25xx_dai_driver));
+
+	return ret;
+}
+
+int tas25xx_deregister_codec(struct tas25xx_priv *p_tas25xx)
+{
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	snd_soc_unregister_component(plat_data->dev);
+
+	return 0;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS25XX ALSA SOC Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
+

+ 1472 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/src/tas25xx-logic.c

@@ -0,0 +1,1472 @@
+/*
+ * ALSA SoC Texas Instruments TAS25XX High Performance 4W Smart Amplifier
+ *
+ * Copyright (C) 2021 Texas Instruments, Inc.
+ *
+ * Author: Niranjan H Y, Vijeth P O
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+#include <linux/power_supply.h>
+#include "../inc/tas25xx-logic.h"
+#include "../inc/tas25xx-device.h"
+#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
+#include "../algo/inc/tas_smart_amp_v2.h"
+#include "../algo/inc/tas25xx-calib.h"
+#if IS_ENABLED(CONFIG_TISA_SYSFS_INTF)
+#include "../algo/src/tas25xx-sysfs-debugfs-utils.h"
+#endif
+#endif /* CONFIG_TAS25XX_ALGO*/
+#include "../inc/tas25xx-regmap.h"
+#include "../inc/tas25xx-regbin-parser.h"
+#include "../inc/tas25xx-ext.h"
+
+#ifndef DEFAULT_AMBIENT_TEMP
+#define DEFAULT_AMBIENT_TEMP 20
+#endif
+
+/* 128 Register Map to be used during Register Dump*/
+#define REG_CAP_MAX	128
+
+const char *tas_power_states_str[] = {
+	"TAS_POWER_ACTIVE",
+	"TAS_POWER_MUTE",
+	"TAS_POWER_SHUTDOWN",
+};
+
+static struct tas25xx_reg regs[REG_CAP_MAX] = {
+	{0,	0},	{1,	0},	{2,	0},	{3,	0},	{4,	0},
+	{5,	0},	{6,	0},	{7,	0},	{8,	0},	{9,	0},
+	{10,	0},	{11,	0},	{12,	0},	{13,	0},	{14,	0},
+	{15,	0},	{16,	0},	{17,	0},	{18,	0},	{19,	0},
+	{20,	0},	{21,	0},	{22,	0},	{23,	0},	{24,	0},
+	{25,	0},	{26,	0},	{27,	0},	{28,	0},	{29,	0},
+	{30,	0},	{31,	0},	{32,	0},	{33,	0},	{34,	0},
+	{35,	0},	{36,	0},	{37,	0},	{38,	0},	{39,	0},
+	{40,	0},	{41,	0},	{42,	0},	{43,	0},	{44,	0},
+	{45,	0},	{46,	0},	{47,	0},	{48,	0},	{49,	0},
+	{50,	0},	{51,	0},	{52,	0},	{53,	0},	{54,	0},
+	{55,	0},	{56,	0},	{57,	0},	{58,	0},	{59,	0},
+	{60,	0},	{61,	0},	{62,	0},	{63,	0},	{64,	0},
+	{65,	0},	{66,	0},	{67,	0},	{68,	0},	{69,	0},
+	{70,	0},	{71,	0},	{72,	0},	{73,	0},	{74,	0},
+	{75,	0},	{76,	0},	{77,	0},	{78,	0},	{79,	0},
+	{80,	0},	{81,	0},	{82,	0},	{83,	0},	{84,	0},
+	{85,	0},	{86,	0},	{87,	0},	{88,	0},	{89,	0},
+	{90,	0},	{91,	0},	{92,	0},	{93,	0},	{94,	0},
+	{95,	0},	{96,	0},	{97,	0},	{98,	0},	{99,	0},
+	{100,	0},	{101,	0},	{102,	0},	{103,	0},	{104,	0},
+	{105,	0},	{106,	0},	{107,	0},	{108,	0},	{109,	0},
+	{110,	0},	{111,	0},	{112,	0},	{113,	0},	{114,	0},
+	{115,	0},	{116,	0},	{117,	0},	{118,	0},	{119,	0},
+	{120,	0},	{121,	0},	{122,	0},	{123,	0},	{124,	0},
+	{125,	0},	{126,	0},	{127,	0},
+};
+
+static void (*tas_amp_err_fptr)(int32_t i2c, int32_t err);
+
+void tas25xx_register_amp_error_callback(void (*amp_err_cb)(int32_t ch, int32_t err))
+{
+	tas_amp_err_fptr = amp_err_cb;
+}
+EXPORT_SYMBOL_GPL(tas25xx_register_amp_error_callback);
+
+static void tas25xx_post_amp_err_to_platform(int32_t i2c, int32_t err)
+{
+	if (tas_amp_err_fptr)
+		tas_amp_err_fptr(i2c, err);
+}
+
+int tas25xx_change_book(struct tas25xx_priv *p_tas25xx,
+	int32_t chn, int book)
+{
+	int ret = -EINVAL;
+
+	if (chn >= p_tas25xx->ch_count)
+		return ret;
+
+	ret = 0;
+	if (p_tas25xx->devs[chn]->mn_current_book != book) {
+		ret = p_tas25xx->plat_write(
+				p_tas25xx->platform_data,
+				p_tas25xx->devs[chn]->mn_addr,
+				TAS25XX_BOOKCTL_PAGE, 0, chn);
+		if (ret) {
+			pr_err("%s, ERROR, L=%d, E=%d, ch=%d\n",
+				__func__, __LINE__, ret, chn);
+		} else {
+			ret = p_tas25xx->plat_write(
+					p_tas25xx->platform_data,
+					p_tas25xx->devs[chn]->mn_addr,
+					TAS25XX_BOOKCTL_REG, book, chn);
+			if (ret) {
+				pr_err("%s, ERROR, L=%d, E=%d, ch=%d\n",
+					__func__, __LINE__, ret, chn);
+			}
+		}
+
+		if (!ret)
+			p_tas25xx->devs[chn]->mn_current_book = book;
+	}
+
+	return ret;
+}
+
+
+/* Function to Dump Registers */
+void tas25xx_dump_regs(struct tas25xx_priv  *p_tas25xx, int chn)
+{
+	int i;
+	struct linux_platform *plat_data;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	for (i = 0; i < REG_CAP_MAX; i++)
+		p_tas25xx->read(p_tas25xx, chn, regs[i].reg_index, &(regs[i].reg_val));
+
+	dev_err(plat_data->dev, "--- TAS25XX Channel-%d RegDump ---\n", chn);
+	for (i = 0; i < REG_CAP_MAX/16; i++) {
+		dev_err(plat_data->dev,
+			"%02x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+			regs[16 * i].reg_index, regs[16 * i].reg_val, regs[16 * i + 1].reg_val,
+			regs[16 * i + 2].reg_val, regs[16 * i + 3].reg_val,
+			regs[16 * i + 4].reg_val, regs[16 * i + 5].reg_val,
+			regs[16 * i + 6].reg_val, regs[16 * i + 7].reg_val,
+			regs[16 * i + 8].reg_val, regs[16 * i + 9].reg_val,
+			regs[16 * i + 10].reg_val, regs[16 * i + 11].reg_val,
+			regs[16 * i + 12].reg_val, regs[16 * i + 13].reg_val,
+			regs[16 * i + 14].reg_val, regs[16 * i + 15].reg_val);
+	}
+	if (REG_CAP_MAX % 16) {
+		for (i = 16 * (REG_CAP_MAX / 16); i < REG_CAP_MAX; i++)
+			dev_err(plat_data->dev, "%02x: %02x\n", regs[i].reg_index,
+				regs[i].reg_val);
+	}
+	dev_err(plat_data->dev, "--- TAS25XX Channel-%d RegDump done ---\n", chn);
+}
+
+/*TODO: Revisit the function as its not usually used*/
+static void tas25xx_hard_reset(struct tas25xx_priv  *p_tas25xx)
+{
+	int i = 0;
+
+	p_tas25xx->hw_reset(p_tas25xx);
+
+	for (i = 0; i < p_tas25xx->ch_count; i++)
+		p_tas25xx->devs[i]->mn_current_book = -1;
+
+	if (p_tas25xx->mn_err_code)
+		pr_info("%s: before reset, ErrCode=0x%x\n", __func__,
+			p_tas25xx->mn_err_code);
+	p_tas25xx->mn_err_code = 0;
+}
+
+int tas25xx_set_dai_fmt_for_fmt(struct tas25xx_priv *p_tas25xx, unsigned int fmt)
+{
+	int ret;
+	int i;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		dev_info(plat_data->dev, "SND_SOC_DAIFMT_CBS_CFS\n");
+		break;
+	default:
+		dev_err(plat_data->dev, "ASI format mask is not found\n");
+		ret = -EINVAL;
+		break;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		dev_info(plat_data->dev, "INV format: NBNF\n");
+		for (i = 0; i < p_tas25xx->ch_count; i++) {
+			ret = tas25xx_set_fmt_inv(p_tas25xx, i, FMT_INV_NB_NF);
+			if (ret) {
+				dev_err(plat_data->dev,
+					"Error setting the format FMT_INV_NB_NF for ch=%d\n", i);
+				break;
+			}
+		}
+		break;
+
+	case SND_SOC_DAIFMT_IB_NF:
+		dev_info(plat_data->dev, "INV format: IBNF\n");
+		for (i = 0; i < p_tas25xx->ch_count; i++) {
+			ret = tas25xx_set_fmt_inv(p_tas25xx, i, FMT_INV_IB_NF);
+			if (ret) {
+				dev_err(plat_data->dev,
+					"Error setting the format FMT_INV_IB_NF for ch=%d\n", i);
+				break;
+			}
+		}
+		break;
+
+	case SND_SOC_DAIFMT_NB_IF:
+		dev_info(plat_data->dev, "INV format: NBIF\n");
+		for (i = 0; i < p_tas25xx->ch_count; i++) {
+			ret = tas25xx_set_fmt_inv(p_tas25xx, i, FMT_INV_NB_IF);
+			if (ret) {
+				dev_err(plat_data->dev,
+					"Error setting the format FMT_INV_NB_IF for ch=%d\n", i);
+				break;
+			}
+		}
+		break;
+
+	case SND_SOC_DAIFMT_IB_IF:
+		dev_info(plat_data->dev, "INV format: IBIF\n");
+		for (i = 0; i < p_tas25xx->ch_count; i++) {
+			ret = tas25xx_set_fmt_inv(p_tas25xx, i, FMT_INV_IB_IF);
+			if (ret) {
+				dev_err(plat_data->dev,
+					"Error setting the format FMT_INV_IB_IF for ch=%d\n", i);
+				break;
+			}
+		}
+		break;
+
+	default:
+		dev_err(plat_data->dev, "ASI format Inverse is not found\n");
+		ret = -EINVAL;
+	}
+
+	if (ret)
+		return ret;
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case (SND_SOC_DAIFMT_I2S):
+		dev_info(plat_data->dev,
+			"SND_SOC_DAIFMT_I2S tdm_rx_start_slot = 1\n");
+		for (i = 0; i < p_tas25xx->ch_count; i++) {
+			ret = tas25xx_set_fmt_mask(p_tas25xx, i, FMT_MASK_I2S);
+			if (ret) {
+				dev_err(plat_data->dev,
+					"FMT_MASK_I2S set failed for ch=%d\n", i);
+				break;
+			}
+		}
+
+		break;
+
+	case (SND_SOC_DAIFMT_DSP_A):
+		dev_info(plat_data->dev,
+			"SND_SOC_DAIFMT_DSP_A tdm_rx_start_slot =1\n");
+		for (i = 0; i < p_tas25xx->ch_count; i++) {
+			ret = tas25xx_set_fmt_mask(p_tas25xx, i, FMT_MASK_DSP_A);
+			if (ret) {
+				dev_err(plat_data->dev,
+					"FMT_MASK_DSP_A set failed for ch=%d\n", i);
+				break;
+			}
+		}
+		break;
+
+	case (SND_SOC_DAIFMT_DSP_B):
+		dev_info(plat_data->dev,
+			"SND_SOC_DAIFMT_DSP_B tdm_rx_start_slot = 0\n");
+		for (i = 0; i < p_tas25xx->ch_count; i++) {
+			ret = tas25xx_set_fmt_mask(p_tas25xx, i, FMT_MASK_DSP_B);
+			if (ret) {
+				dev_err(plat_data->dev,
+					"FMT_MASK_DSP_B set failed for ch=%d\n", i);
+				break;
+			}
+		}
+		break;
+
+	case (SND_SOC_DAIFMT_LEFT_J):
+		dev_info(plat_data->dev,
+			"SND_SOC_DAIFMT_LEFT_J tdm_rx_start_slot = 0\n");
+		for (i = 0; i < p_tas25xx->ch_count; i++) {
+			ret = tas25xx_set_fmt_mask(p_tas25xx, i, FMT_MASK_LEFT_J);
+			if (ret) {
+				dev_err(plat_data->dev,
+					" set failed for ch=%d\n", i);
+				break;
+			}
+		}
+		break;
+
+	default:
+		dev_err(plat_data->dev, "DAI Format is not found, fmt=0x%x\n", fmt);
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * This shall be called during the middle of the playback.
+ * So all the register settings should be restored back to their original settings.
+ */
+int tas25xx_reinit(struct tas25xx_priv *p_tas25xx)
+{
+	int i;
+	int ret;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	ret = tas_write_init_config_params(p_tas25xx,
+			p_tas25xx->ch_count);
+	if (ret) {
+		dev_err(plat_data->dev, "Failed to initialize, error=%d\n", ret);
+		return ret;
+	}
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		ret = tas25xx_set_init_params(p_tas25xx, i);
+		if (ret < 0) {
+			dev_err(plat_data->dev, "%s error while initialisation for ch=%d\n",
+				__func__, i);
+			break;
+		}
+	}
+
+	if (ret)
+		goto reinit_done;
+
+	/* set dai fmt*/
+	if (p_tas25xx->mn_fmt) {
+		ret = tas25xx_set_dai_fmt_for_fmt(p_tas25xx, p_tas25xx->mn_fmt);
+		if (ret)
+			goto reinit_done;
+	}
+
+	/* hw params */
+	if (p_tas25xx->mn_fmt_mode == 2) {
+		/* TDM mode */
+		ret = tas25xx_set_tdm_rx_slot(p_tas25xx,
+			p_tas25xx->ch_count, p_tas25xx->mn_rx_width);
+		if (ret) {
+			dev_err(plat_data->dev, "%s failed to set Rx slots\n", __func__);
+			goto reinit_done;
+		}
+
+		ret = tas25xx_set_tdm_tx_slot(p_tas25xx,
+			p_tas25xx->ch_count, p_tas25xx->mn_tx_slot_width);
+		if (ret) {
+			dev_err(plat_data->dev, "%s failed to set Tx slots\n", __func__);
+			goto reinit_done;
+		}
+	} else if (p_tas25xx->mn_fmt_mode == 1) {
+		/* I2S mode */
+		for (i = 0; i < p_tas25xx->ch_count; i++) {
+			ret = tas25xx_rx_set_bitwidth(p_tas25xx, p_tas25xx->mn_rx_width, i);
+			if (ret) {
+				dev_err(plat_data->dev,
+					"Error=%d while setting rx bitwidth, ch=%d\n", ret, i);
+				break;
+			}
+		}
+		if (ret)
+			goto reinit_done;
+
+		ret = tas25xx_iv_vbat_slot_config(p_tas25xx, p_tas25xx->mn_tx_slot_width);
+		if (ret) {
+			dev_err(plat_data->dev, "Error=%d while IV vbat slot config %s\n",
+				ret, __func__);
+			goto reinit_done;
+		}
+	}
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		ret = tas25xx_set_sample_rate(p_tas25xx, i, p_tas25xx->sample_rate);
+		if (ret) {
+			dev_err(plat_data->dev, "%s: Error=%d setting sample rate\n",
+				__func__, ret);
+			break;
+		}
+	}
+	if (ret)
+		goto reinit_done;
+
+	ret = tas25xx_update_kcontrol_data(p_tas25xx, KCNTR_ANYTIME, 0xFFFF);
+	if (ret)
+		goto reinit_done;
+
+
+reinit_done:
+	return ret;
+
+}
+
+#if IS_ENABLED(CONFIG_TAS25XX_IRQ_BD)
+void tas25xx_clear_interrupt_stats(struct tas25xx_priv *p_tas25xx)
+{
+	int i, j;
+	struct tas25xx_intr_info *intr_info;
+	struct tas25xx_interrupts *intr_data;
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		intr_data = &p_tas25xx->intr_data[i];
+		for (j = 0; j < intr_data->count; j++) {
+			intr_info = &intr_data->intr_info[j];
+			intr_info->count = 0;
+		}
+	}
+}
+
+void tas25xx_log_interrupt_stats(struct tas25xx_priv *p_tas25xx)
+{
+	int i, j;
+	static u8 log_once;
+	struct tas25xx_intr_info *intr_info;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+	struct tas25xx_interrupts *intr_data;
+
+	if (!log_once) {
+		dev_info(plat_data->dev,
+			"irq-bigdata: ||ch\t||name\t||count(total) ||\n");
+		log_once = 1;
+	}
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		intr_data = &p_tas25xx->intr_data[i];
+		for (j = 0; j < intr_data->count; j++) {
+			intr_info = &intr_data->intr_info[j];
+			if (intr_info->count || intr_info->count_persist)
+				dev_info(plat_data->dev,
+					"irq-bigdata: |%d |%s |%u(%llu) |\n", i,
+					intr_info->name, intr_info->count,
+					intr_info->count_persist);
+		}
+	}
+}
+#endif
+
+/*
+ * called with codec lock held
+ */
+int tas25xx_irq_work_func(struct tas25xx_priv *p_tas25xx)
+{
+	int8_t clk_intr;
+	int8_t othr_intr;
+	int32_t i, j;
+	int32_t ret = 0;
+	int32_t type = 0;
+	int32_t irq_lim_crossed;
+	int32_t interrupt_count;
+	int32_t state;
+	int32_t int_actions, power_on_required;
+	uint32_t intr_detected = 0;
+	int32_t reset_done = 0;
+	struct tas_device *tasdev;
+	struct linux_platform *plat_data;
+	struct tas25xx_interrupts *intr_data;
+	struct tas25xx_intr_info *intr_info;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	p_tas25xx->disable_irq(p_tas25xx);
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		ret = tas_dev_interrupt_read(p_tas25xx, i, &type);
+		if (ret)
+			intr_detected |= (1 << i);
+	}
+	p_tas25xx->enable_irq(p_tas25xx);
+
+	if (intr_detected == 0) {
+		if (is_power_up_state(p_tas25xx->m_power_state))
+			for (i = 0; i < p_tas25xx->ch_count; i++)
+				tas25xx_dump_regs(p_tas25xx, i);
+		return ret;
+	}
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		if ((intr_detected & (1 << i)) == 0)
+			continue;
+
+		intr_data = &p_tas25xx->intr_data[i];
+		interrupt_count = intr_data->count;
+
+		irq_lim_crossed = 0;
+
+		tasdev = p_tas25xx->devs[i];
+
+#if IS_ENABLED(CONFIG_TISA_SYSFS_INTF)
+		tas25xx_algo_bump_oc_count(0, 0);
+#endif
+		ret = tas_dev_interrupt_disable(p_tas25xx, i);
+		if (tasdev->irq_count != 0) {
+			if (time_after(jiffies, tasdev->jiffies +
+				msecs_to_jiffies(TAS25XX_IRQ_DET_TIMEOUT))) {
+				tasdev->jiffies = jiffies;
+				tasdev->irq_count = 0;
+			} else {
+				tasdev->irq_count++;
+				if (tasdev->irq_count > TAS25XX_IRQ_DET_CNT_LIMIT) {
+					dev_err(plat_data->dev,
+						"INTR: %s: ch=%d continuous interrupt detected %d\n",
+						__func__, i, tasdev->irq_count);
+					tas25xx_dump_regs(p_tas25xx, i);
+					irq_lim_crossed = 1;
+				}
+			}
+		} else {
+			tasdev->jiffies = jiffies;
+			tasdev->irq_count = 1;
+		}
+
+		if (irq_lim_crossed)
+			continue;
+
+		ret = tas_dev_interrupt_clear(p_tas25xx, i);
+		if (ret)
+			dev_warn(plat_data->dev,
+				"%s Unable to clear interrupt, ch=%d", __func__, i);
+
+		clk_intr = 0;
+		othr_intr = 0;
+		power_on_required = 1;
+		int_actions = 0;
+		for (j = 0; j < interrupt_count; j++) {
+			intr_info = &intr_data->intr_info[j];
+			if (intr_info->detected) {
+				dev_err(plat_data->dev,
+					"ch=%d Interrupt action for the detected interrupt %s is %d",
+					i, intr_info->name, intr_info->action);
+				int_actions |= intr_info->action;
+				if (intr_info->action & TAS_INT_ACTION_POWER_ON)
+					power_on_required = power_on_required & 1;
+				else
+					power_on_required = power_on_required & 0;
+
+				if (intr_info->is_clock_based)
+					clk_intr = 1;
+				else
+					othr_intr = 1;
+
+				if (intr_info->notify_int_val) {
+					dev_err(plat_data->dev, "ch=%d INTR: %s Notify: %d",
+						i, intr_info->name, intr_info->notify_int_val);
+					tas25xx_post_amp_err_to_platform(p_tas25xx->devs[i]->mn_addr,
+						intr_info->notify_int_val);
+				}
+
+				/* reset to not detected after detection */
+				intr_info->detected = 0;
+			}
+		}
+
+		dev_info(plat_data->dev, "ch=%d INTR force power on?(y/n):%s",
+			i, power_on_required ? "y":"n");
+		if (!power_on_required) {
+			if (othr_intr)
+				ret = tas25xx_set_power_state(p_tas25xx, TAS_POWER_SHUTDOWN, 1<<i);
+			continue;
+		}
+
+		/*
+		 * ||clk||oth|| action ||
+		 * | 0 | 1 | power down |
+		 * | 1 | 0 | do absolutely nothing |
+		 * | 1 | 1 | power down |
+		 */
+		if (clk_intr && !othr_intr) {
+			int_actions = int_actions & ~TAS_INT_ACTION_POWER_ON;
+			dev_info(plat_data->dev,
+				"ch=%d INTR power on ignored for [clk=%d oth=%d]",
+				i, clk_intr, othr_intr);
+		}
+
+		/* order should be followed */
+		if (int_actions & TAS_INT_ACTION_HW_RESET) {
+			dev_info(plat_data->dev, "ch=%d Interrupt action hard reset", i);
+			tas25xx_hard_reset(p_tas25xx);
+			reset_done = 1;
+		}
+
+		if (int_actions & TAS_INT_ACTION_SW_RESET) {
+			dev_info(plat_data->dev, "ch=%d Interrupt action software reset", i);
+			ret = tas25xx_software_reset(p_tas25xx, i);
+			if (ret)
+				dev_err(plat_data->dev,
+					"ch=%d Software reset failed error=%d", i, ret);
+			reset_done = 1;
+		}
+
+		if (reset_done)
+			ret = tas25xx_reinit(p_tas25xx);
+
+		if (int_actions & TAS_INT_ACTION_POWER_ON) {
+			tas25xx_check_if_powered_on(p_tas25xx, &state, i);
+			if (state == 0) {
+				/* interrupts are enabled during power up sequence */
+				dev_info(plat_data->dev,
+					"ch=%d Try powering on the device", i);
+				ret = tas25xx_set_power_state(p_tas25xx,
+					TAS_POWER_ACTIVE, (1<<i));
+			} else {
+				dev_info(plat_data->dev,
+					"ch=%d Already powered on, Enable the interrupt", i);
+				ret = tas_dev_interrupt_enable(p_tas25xx, i);
+			}
+		}
+	}
+
+	return ret;
+}
+
+int tas25xx_init_work_func(struct tas25xx_priv *p_tas25xx, struct tas_device *dev_tas25xx)
+{
+	int chn = 0;
+	int ret = 0;
+	int detected = 0;
+	int type = 0;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	chn = dev_tas25xx->channel_no;
+
+	dev_info(plat_data->dev, "ch=%d %s\n", chn, __func__);
+	ret = tas25xx_set_post_powerup(p_tas25xx, chn);
+	if (ret)
+		dev_err(plat_data->dev,
+			"ch=%d Error in  post powerup data write.  err=%d\n",
+			chn, ret);
+
+	/* check for interrupts during power up */
+	detected = tas_dev_interrupt_read(p_tas25xx, chn, &type);
+	if (detected) {
+		if (type == INTERRUPT_TYPE_CLOCK_BASED) {
+			/* ignore clock based interrupts which we are monitoring */
+			dev_warn(plat_data->dev,
+				"Ignoring clock based interrupts and clear latch");
+	ret = tas_dev_interrupt_clear(p_tas25xx, chn);
+	if (ret)
+		dev_err(plat_data->dev,
+			"ch=%d Error while clearing interrup err=%d\n",
+			chn, ret);
+		} else {
+			dev_err(plat_data->dev,
+				"Non clock based interrupts detected, skip latch clear for recovery");
+		}
+	}
+
+	/* enabling the interrupt here to avoid any clock errors during the bootup*/
+	ret = tas_dev_interrupt_enable(p_tas25xx, chn);
+	if (ret)
+		dev_err(plat_data->dev,
+			"ch=%d %s: Failed to enable interrupt\n", chn, __func__);
+
+	return ret;
+}
+
+int tas25xx_dc_work_func(struct tas25xx_priv *p_tas25xx, int chn)
+{
+	pr_info("%s: ch %d\n", __func__, chn);
+	//tas25xx_reload(p_tas25xx, chn);
+
+	return 0;
+}
+
+int tas25xx_register_device(struct tas25xx_priv  *p_tas25xx)
+{
+	int i = 0;
+
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	dev_info(plat_data->dev, "%s:\n", __func__);
+
+	tas25xx_hard_reset(p_tas25xx);
+
+	for (i = 0; i < p_tas25xx->ch_count; i++)
+		p_tas25xx->devs[i]->channel_no = i;
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_TAS25XX_IRQ_BD)
+static ssize_t irq_bd_show(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int8_t found = 0;
+
+	int32_t i, j;
+	struct tas25xx_interrupts *intr_data;
+	struct tas25xx_intr_info *intr_info;
+	struct tas25xx_priv *p_tas25xx = dev_get_drvdata(dev);
+
+	if (!p_tas25xx) {
+		dev_info(dev, "dev_get_drvdata returned NULL");
+		return -EINVAL;
+	}
+
+	intr_info = NULL;
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		intr_data = &p_tas25xx->intr_data[i];
+		for (j = 0; j < intr_data->count; j++) {
+			intr_info = &intr_data->intr_info[j];
+			if (attr == intr_info->dev_attr) {
+				found = 1;
+				break;
+			}
+		}
+	}
+
+	if (found)
+		return snprintf(buf, 32, "count=%u\npersist=%llu\n",
+			intr_info->count, intr_info->count_persist);
+	else
+		return snprintf(buf, 32, "something went wrong!!!");
+
+}
+
+static struct attribute_group s_attribute_group = {
+	.attrs = NULL,
+};
+
+int tas_smartamp_add_irq_bd(struct tas25xx_priv *p_tas25xx)
+{
+	int i, j, k;
+	int ret;
+	int total_irqs = 0;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+	struct device_attribute *p_irqpd;
+	struct attribute **attribute_array;
+	struct tas25xx_intr_info *intr_info;
+	struct tas25xx_interrupts *intr_data;
+	struct device *irq_dev;
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		intr_data = &p_tas25xx->intr_data[i];
+		total_irqs += intr_data->count;
+	}
+
+	p_irqpd = (struct device_attribute *)kzalloc(total_irqs *
+			sizeof(struct device_attribute), GFP_KERNEL);
+	if (!p_irqpd)
+		return -ENOMEM;
+
+	total_irqs++;
+	attribute_array = kzalloc((sizeof(struct attribute *) * total_irqs), GFP_KERNEL);
+
+	k = 0;
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		intr_data = &p_tas25xx->intr_data[i];
+		for (j = 0; j < intr_data->count; j++) {
+			intr_info = &intr_data->intr_info[j];
+			p_irqpd[k].attr.name = intr_info->name;
+			p_irqpd[k].attr.mode = 0664;
+			p_irqpd[k].show = irq_bd_show;
+			p_irqpd[k].store = NULL;
+
+			intr_info->dev_attr = &p_irqpd[k];
+			attribute_array[k] = &p_irqpd[k].attr;
+			k++;
+		}
+	}
+	s_attribute_group.attrs = attribute_array;
+
+	irq_dev = device_create(p_tas25xx->class,
+				NULL, 1, NULL, "irqs");
+	if (IS_ERR(irq_dev)) {
+		dev_err(plat_data->dev,
+			"%sFailed to create irqs\n", __func__);
+		ret = PTR_ERR(irq_dev);
+		irq_dev = NULL;
+		goto err_irqbd;
+	}
+
+	ret = sysfs_create_group(&irq_dev->kobj,
+		&s_attribute_group);
+	if (ret) {
+		dev_err(plat_data->dev,
+			"%sFailed to create sysfs group\n", __func__);
+		goto err_irqbd;
+	}
+
+	p_tas25xx->irqdata.p_dev_attr = p_irqpd;
+	p_tas25xx->irqdata.p_attr_arr = attribute_array;
+	p_tas25xx->irqdata.irq_dev = irq_dev;
+
+	dev_set_drvdata(irq_dev, p_tas25xx);
+
+	dev_info(plat_data->dev, "%s ret=%d\n", __func__, ret);
+
+	return ret;
+
+err_irqbd:
+	kfree(p_irqpd);
+	kfree(attribute_array);
+	return ret;
+}
+
+static void tas_smartamp_remove_irq_bd(struct tas25xx_priv *p_tas25xx)
+{
+	struct irq_bigdata *bd = &p_tas25xx->irqdata;
+
+	if (bd->irq_dev)
+		sysfs_remove_group(&bd->irq_dev->kobj,
+			&s_attribute_group);
+
+	kfree(bd->p_dev_attr);
+	kfree(bd->p_attr_arr);
+
+	memset(bd, 0, sizeof(struct irq_bigdata));
+}
+#endif
+
+enum cmd_type_t {
+	CALIB,
+	TEMP,
+	IV_VBAT,
+	DRV_OPMODE,
+};
+
+static const char *cmd_str_arr[MAX_CMD_LIST] = {
+	"calib",
+	"temp",
+	"iv_vbat",
+	"drv_opmode",
+	NULL,
+};
+
+static struct device_attribute *cmd_arr[MAX_CMD_LIST] = {
+	NULL,
+};
+
+static uint8_t tas25xx_get_amb_temp(void)
+{
+	struct power_supply *psy;
+	union power_supply_propval value = {0};
+
+	psy = power_supply_get_by_name("battery");
+	if (!psy || !psy->desc || !psy->desc->get_property) {
+		pr_err("[TI-SmartPA:%s] getting ambient temp failed, using default value %d\n",
+			__func__, DEFAULT_AMBIENT_TEMP);
+		return DEFAULT_AMBIENT_TEMP;
+	}
+	psy->desc->get_property(psy, POWER_SUPPLY_PROP_TEMP, &value);
+
+	return DIV_ROUND_CLOSEST(value.intval, 10);
+}
+
+static ssize_t cmd_show(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	int32_t i, cmd_count, found = 0, ret = -EINVAL, temp;
+
+	struct tas25xx_priv *p_tas25xx = dev_get_drvdata(dev);
+
+	if (!p_tas25xx) {
+		dev_info(dev,
+			"%s get_drvdata returned NULL", __func__);
+		return ret;
+	}
+
+	cmd_count = ARRAY_SIZE(cmd_arr);
+
+	for (i = 0; i < cmd_count; i++) {
+		if (attr == cmd_arr[i]) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		switch (i) {
+		case TEMP:
+			temp = tas25xx_get_amb_temp();
+			ret = snprintf(buf, 32, "%d", temp);
+		break;
+
+		case IV_VBAT:
+			temp = ((p_tas25xx->curr_mn_iv_width & 0xFFFF) |
+				((p_tas25xx->curr_mn_vbat & 0xFFFF) << 16));
+			ret = snprintf(buf, 32, "0x%x", temp);
+		break;
+
+		case DRV_OPMODE:
+			temp = tas25xx_get_drv_channel_opmode();
+			ret = snprintf(buf, 32, "0x%x", temp);
+		break;
+
+		default:
+			ret = snprintf(buf, 32, "unsupported cmd %d", i);
+		break;
+		}
+	}
+
+	return ret;
+}
+
+static ssize_t cmd_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t size)
+{
+	int32_t i, cmd_count, found = 0, ret = -EINVAL;
+	struct tas25xx_priv *p_tas25xx = dev_get_drvdata(dev);
+
+	if (!p_tas25xx) {
+		dev_info(dev, "%s drv_data is null", __func__);
+		return ret;
+	}
+
+	cmd_count = ARRAY_SIZE(cmd_arr);
+
+	for (i = 0; i < cmd_count; i++) {
+		if (attr == cmd_arr[i]) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		if (i == CALIB) {
+			if (!strncmp(buf, "cal_init_blk", strlen("cal_init_blk")))
+				tas25xx_prep_dev_for_calib(1);
+			else if (!strncmp(buf, "cal_deinit_blk", strlen("cal_deinit_blk")))
+				tas25xx_prep_dev_for_calib(0);
+			else
+				dev_info(dev,
+					"%s Not supported %s, for calib", __func__, buf);
+		} else {
+			dev_info(dev, "%s Not supported %s", __func__, buf);
+		}
+	} else {
+		dev_info(dev, "%s Not supported %s", __func__, buf);
+	}
+
+	return size;
+}
+
+static struct attribute_group cmd_attribute_group = {
+	.attrs = NULL,
+};
+
+int tas_smartamp_add_cmd_intf(struct tas25xx_priv *p_tas25xx)
+{
+	int i, cmd_count, ret;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+	struct device_attribute *p_attr_arr;
+	struct attribute **attr_arry;
+	struct device *cmd_dev;
+
+	cmd_count = ARRAY_SIZE(cmd_arr);
+
+	p_attr_arr = (struct device_attribute *)kzalloc(cmd_count *
+			sizeof(struct device_attribute), GFP_KERNEL);
+	if (!p_attr_arr) {
+		attr_arry = NULL;
+		ret = -ENOMEM;
+		goto err_cmd;
+	}
+
+	attr_arry = kzalloc((sizeof(struct attribute *) *
+					cmd_count), GFP_KERNEL);
+	if (!attr_arry) {
+		ret = -ENOMEM;
+		goto err_cmd;
+	}
+
+	for (i = 0; (i < cmd_count) && cmd_str_arr[i]; i++) {
+		p_attr_arr[i].attr.name = cmd_str_arr[i];
+		p_attr_arr[i].attr.mode = 0664;
+		p_attr_arr[i].show = cmd_show;
+		p_attr_arr[i].store = cmd_store;
+
+		cmd_arr[i] = &p_attr_arr[i];
+		attr_arry[i] = &p_attr_arr[i].attr;
+	}
+	cmd_attribute_group.attrs = attr_arry;
+
+	cmd_dev = device_create(p_tas25xx->class,
+				NULL, 1, NULL, "cmd");
+	if (IS_ERR(cmd_dev)) {
+		dev_err(plat_data->dev,
+			"%sFailed to create cmds\n", __func__);
+		ret = PTR_ERR(cmd_dev);
+		goto err_cmd;
+	}
+
+	ret = sysfs_create_group(&cmd_dev->kobj,
+		&cmd_attribute_group);
+	if (ret) {
+		dev_err(plat_data->dev,
+			"%s Failed to create sysfs group\n", __func__);
+		goto err_cmd;
+	}
+
+	p_tas25xx->cmd_data.p_dev_attr = p_attr_arr;
+	p_tas25xx->cmd_data.p_attr_arr = attr_arry;
+	p_tas25xx->cmd_data.cmd_dev = cmd_dev;
+
+	dev_set_drvdata(cmd_dev, p_tas25xx);
+
+	dev_info(plat_data->dev, "%s ret=%d\n", __func__, ret);
+
+	return ret;
+
+err_cmd:
+	kfree(p_attr_arr);
+	kfree(attr_arry);
+	return ret;
+}
+
+static void tas_smartamp_remove_cmd_intf(struct tas25xx_priv *p_tas25xx)
+{
+	struct cmd_data *cmd_data = &p_tas25xx->cmd_data;
+
+	if (cmd_data->cmd_dev) {
+		sysfs_remove_group(&cmd_data->cmd_dev->kobj,
+			&cmd_attribute_group);
+	}
+
+	kfree(cmd_data->p_dev_attr);
+	kfree(cmd_data->p_attr_arr);
+
+	memset(cmd_data, 0, sizeof(struct cmd_data));
+}
+
+int tas_smartamp_add_sysfs(struct tas25xx_priv *p_tas25xx)
+{
+	int ret = 0;
+	struct class *class = NULL;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	class = class_create(THIS_MODULE, "tas25xx_dev");
+	if (IS_ERR(class)) {
+		ret = PTR_ERR(class);
+		dev_err(plat_data->dev,
+			"%s err class create %d\n", __func__, ret);
+		class = NULL;
+	}
+
+	if (class) {
+		p_tas25xx->class = class;
+#if IS_ENABLED(CONFIG_TAS25XX_IRQ_BD)
+		ret = tas_smartamp_add_irq_bd(p_tas25xx);
+		if (ret)
+			dev_err(plat_data->dev,
+				"%s err registring irqbd %d\n", __func__, ret);
+#endif
+		tas_smartamp_add_cmd_intf(p_tas25xx);
+	}
+
+	return ret;
+}
+
+void tas_smartamp_remove_sysfs(struct tas25xx_priv *p_tas25xx)
+{
+#if IS_ENABLED(CONFIG_TAS25XX_IRQ_BD)
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+#endif
+
+	tas_smartamp_remove_cmd_intf(p_tas25xx);
+#if IS_ENABLED(CONFIG_TAS25XX_IRQ_BD)
+	tas_smartamp_remove_irq_bd(p_tas25xx);
+	dev_info(plat_data->dev,
+			"%s de-registring irqbd done\n", __func__);
+#endif
+
+	if (p_tas25xx->class) {
+		device_destroy(p_tas25xx->class, 1);
+		class_destroy(p_tas25xx->class);
+		p_tas25xx->class = NULL;
+	}
+}
+
+int tas25xx_probe(struct tas25xx_priv *p_tas25xx)
+{
+	int ret = -1, i = 0;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		ret = tas25xx_set_init_params(p_tas25xx, i);
+		if (ret < 0) {
+			dev_err(plat_data->dev, "%s err=%d, initialisation",
+				__func__, ret);
+			goto end;
+		}
+	}
+
+	ret = tas25xx_create_kcontrols(p_tas25xx);
+	if (ret) {
+		dev_warn(plat_data->dev, "%s err=%d creating controls",
+			__func__, ret);
+		ret = 0;
+	}
+
+#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
+	tas_smartamp_add_algo_controls(plat_data->codec, plat_data->dev,
+		p_tas25xx->ch_count);
+#endif
+	tas_smartamp_add_sysfs(p_tas25xx);
+
+end:
+	return ret;
+}
+
+void tas25xx_remove(struct tas25xx_priv *p_tas25xx)
+{
+#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+	tas_smartamp_remove_algo_controls(plat_data->codec);
+#endif
+	/* REGBIN related */
+	tas25xx_remove_binfile(p_tas25xx);
+	tas_smartamp_remove_sysfs(p_tas25xx);
+}
+
+int tas25xx_set_power_state(struct tas25xx_priv *p_tas25xx,
+			enum tas_power_states_t state, uint32_t ch_bitmask)
+{
+	int ret = 0, i = 0;
+	enum tas_power_states_t cur_state;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	dev_info(plat_data->dev, "%s: state %s\n", __func__,
+		(state <= TAS_POWER_SHUTDOWN) ? tas_power_states_str[state] : "INVALID");
+
+	cur_state = p_tas25xx->m_power_state;
+	p_tas25xx->m_power_state = state;
+
+	/* supports max 4 channels */
+	ch_bitmask &= tas25xx_get_drv_channel_opmode() & 0xF;
+
+	switch (state) {
+	case TAS_POWER_ACTIVE:
+		for (i = 0; i < p_tas25xx->ch_count; i++) {
+			if ((ch_bitmask & (1<<i)) == 0)
+				continue;
+
+			if (cur_state != state) {
+				dev_dbg(plat_data->dev,
+					"ch=%d %s: clearing interrupts \n", i, __func__);
+
+				ret = tas_dev_interrupt_clear(p_tas25xx, i);
+				if (ret) {
+					dev_err(plat_data->dev,
+						"ch=%d %s: Error clearing interrupt\n", i, __func__);
+					ret = 0;
+				}
+			} else {
+				dev_dbg(plat_data->dev,
+					"ch=%d %s: skipping clearing interrupts \n", i, __func__);
+			}
+
+			ret = tas25xx_set_pre_powerup(p_tas25xx, i);
+			if (ret) {
+				dev_err(plat_data->dev, "ch=%d %s setting power state failed, err=%d\n",
+					i, __func__, ret);
+			} else {
+				ret = tas25xx_update_kcontrol_data(p_tas25xx, KCNTR_PRE_POWERUP, (1 << i));
+				p_tas25xx->schedule_init_work(p_tas25xx, i);
+			}
+		}
+		break;
+
+	case TAS_POWER_MUTE:
+		for (i = 0; i < p_tas25xx->ch_count; i++) {
+			if ((ch_bitmask & (1<<i)) == 0)
+				continue;
+			tas25xx_set_power_mute(p_tas25xx, i);
+		}
+		break;
+
+	case TAS_POWER_SHUTDOWN:
+		for (i = 0; i < p_tas25xx->ch_count; i++) {
+			if ((ch_bitmask & (1<<i)) == 0)
+				continue;
+
+			/* device interrupt disable */
+			tas_dev_interrupt_disable(p_tas25xx, i);
+			p_tas25xx->cancel_init_work(p_tas25xx, i);
+			ret = tas25xx_set_pre_powerdown(p_tas25xx, i);
+			ret |= tas25xx_set_post_powerdown(p_tas25xx, i);
+		}
+		break;
+
+	default:
+		ret = -EINVAL;
+		dev_err(plat_data->dev, "wrong power state setting %d\n",
+				state);
+	}
+
+	return ret;
+}
+
+int find_fmt_match(struct tas25xx_priv *p_tas25xx, char *fmt, uint8_t **in_out_buf)
+{
+	int ret, i;
+	uint32_t size, blk_count, sublk_sz, fmt_len;
+	uint8_t *buf;
+	struct linux_platform *plat_data;
+
+	ret = -EINVAL;
+	buf = *in_out_buf;
+	plat_data =	(struct linux_platform *) p_tas25xx->platform_data;
+
+	size = *((uint32_t *)buf);
+	buf += sizeof(uint32_t);
+	blk_count = *((uint32_t *)buf);
+	buf += sizeof(uint32_t);
+
+	if (!fmt) {
+		*in_out_buf = NULL;
+		return ret;
+	}
+
+	fmt_len = strlen(fmt);
+	for (i = 0; i < blk_count; i++) {
+		if (memcmp(fmt, buf, fmt_len) == 0) {
+			/* block found */
+			buf += fmt_len;
+			break;
+		} else {
+			/* move to next block */
+			buf += fmt_len;
+			sublk_sz = *((uint32_t *)buf);
+			buf += sizeof(uint32_t) + sublk_sz;
+		}
+	}
+
+	if (i < blk_count) {
+		*in_out_buf = buf;
+		ret = i;
+	} else {
+		*in_out_buf = NULL;
+	}
+
+	return ret;
+}
+
+int get_next_possible_iv_width(int current_iv_width)
+{
+	int ret;
+
+	switch (current_iv_width) {
+	case 16:
+		ret = 12;
+		break;
+
+	case 12:
+		ret = 8;
+		break;
+
+	case 8:
+	default:
+		ret = -1;
+		break;
+	}
+
+	return ret;
+}
+
+int tas25xx_iv_vbat_slot_config(struct tas25xx_priv *p_tas25xx,
+	int mn_slot_width)
+{
+	int i;
+	int ret = -EINVAL;
+	int iv_width, prev_iv_width, vbat_on;
+	int near_match_found = 0;
+	char tx_fmt[32];
+	struct linux_platform *plat_data;
+
+	/* treating 24bit and 32bit as same */
+	if (mn_slot_width == 32)
+		mn_slot_width = 24;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	scnprintf(tx_fmt, 15, "%2d_%s_%02d_%02d_%1d", mn_slot_width,
+		p_tas25xx->mn_fmt_mode == 2 ? "TDM" : "I2S", p_tas25xx->ch_count,
+		p_tas25xx->mn_iv_width, p_tas25xx->mn_vbat);
+
+	dev_info(plat_data->dev, "finding exact match for %s", tx_fmt);
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		uint8_t *data = p_tas25xx->block_op_data[i].tx_fmt_data;
+
+		iv_width = p_tas25xx->mn_iv_width;
+		vbat_on = p_tas25xx->mn_vbat;
+
+		/* Try to find exact match */
+		if ((i > 0) && (near_match_found)) {
+			dev_info(plat_data->dev, "same format is being used %s, ch=%d", tx_fmt, i);
+		} else {
+			scnprintf(tx_fmt, 15, "%2d_%s_%02d_%02d_%1d", mn_slot_width,
+				p_tas25xx->mn_fmt_mode == 2 ? "TDM" : "I2S", p_tas25xx->ch_count,
+				p_tas25xx->mn_iv_width, p_tas25xx->mn_vbat);
+		}
+
+		if (find_fmt_match(p_tas25xx, tx_fmt, &data) >= 0) {
+			/* match found */
+			dev_info(plat_data->dev, "exact match found for %s, ch=%d", tx_fmt, i);
+		} else {
+			/* try near possible option */
+			dev_err(plat_data->dev, "exact match was not found for %s, trying to find near match", tx_fmt);
+			/*1. Try with reduced bit width - 16 -> 12 -> 8 */
+			while (1) {
+				prev_iv_width = iv_width;
+				iv_width = get_next_possible_iv_width(prev_iv_width);
+				if (iv_width < 0) {
+					if (vbat_on) {
+						/*2. reset iv width and check with vbat off */
+						iv_width = p_tas25xx->mn_iv_width;
+						vbat_on = 0;
+					} else {
+						data = NULL;
+						break;
+					}
+				}
+				scnprintf(tx_fmt, 15, "%2d_%s_%02d_%02d_%1d", mn_slot_width,
+					p_tas25xx->mn_fmt_mode == 2 ? "TDM" : "I2S", p_tas25xx->ch_count,
+					iv_width, vbat_on);
+				dev_info(plat_data->dev, "finding near match with %s", tx_fmt);
+				/* reset data for fresh search with new string */
+				data = p_tas25xx->block_op_data[i].tx_fmt_data;
+				if (find_fmt_match(p_tas25xx, tx_fmt, &data) >= 0) {
+					dev_info(plat_data->dev, "near match found: %s for ch=%d", tx_fmt, i);
+					near_match_found |= (1 << i);
+					break;
+				}
+			} /* <while loop> */
+		} /* if-else */
+
+		if (data) {
+			dev_info(plat_data->dev, "possible/exact match found %s for iv=%d, vbat=%d",
+				tx_fmt, p_tas25xx->mn_iv_width, p_tas25xx->mn_vbat);
+			ret = tas25xx_process_block(p_tas25xx, (char *)data, i);
+			/* error setting iv width */
+			if (ret) {
+				dev_info(plat_data->dev, "process block error for %s for iv=%d, vbat=%d",
+					tx_fmt, p_tas25xx->mn_iv_width, p_tas25xx->mn_vbat);
+			}
+		} else {
+			dev_err(plat_data->dev, "no near match found iv=%d, vbat=%d",
+				p_tas25xx->mn_iv_width, p_tas25xx->mn_vbat);
+			ret = -EINVAL;
+			break;
+		}
+	} /* <for loop> */
+
+	if (ret == 0) {
+		p_tas25xx->curr_mn_iv_width = iv_width;
+		p_tas25xx->curr_mn_vbat = vbat_on;
+		p_tas25xx->mn_tx_slot_width = mn_slot_width;
+	}
+
+	return ret;
+}
+
+/* tas25xx_set_bitwidth function is redesigned to accomodate change in
+ * tas25xx_iv_vbat_slot_config()
+ */
+int tas25xx_set_bitwidth(struct tas25xx_priv *p_tas25xx,
+	int bitwidth, int stream)
+{
+	int i;
+	int ret = -EINVAL;
+	struct linux_platform *plat_data;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	dev_info(plat_data->dev, "%s: bitwidth %d stream %d\n", __func__, bitwidth, stream);
+
+	if (stream == TAS25XX_STREAM_PLAYBACK) {
+		for (i = 0; i < p_tas25xx->ch_count; i++) {
+			ret = tas25xx_rx_set_bitwidth(p_tas25xx, bitwidth, i);
+			if (ret) {
+				dev_err(plat_data->dev, "Error =%d while setting bitwidth, ch=%d,",
+					ret, i);
+			}
+		}
+	/*stream == TAS25XX_STREAM_CAPTURE*/
+	} else {
+		ret = tas25xx_iv_vbat_slot_config(p_tas25xx, bitwidth);
+		if (ret)
+			dev_err(plat_data->dev, "Error =%d with %s", ret, __func__);
+	}
+
+	return ret;
+}
+
+/* tas25xx_set_tdm_rx_slot function is redesigned to accomodate change in
+ * tas25xx_iv_vbat_slot_config()
+ */
+int tas25xx_set_tdm_rx_slot(struct tas25xx_priv *p_tas25xx,
+	int slots, int slot_width)
+{
+	int i;
+	int ret = -1;
+	struct linux_platform *plat_data;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	dev_info(plat_data->dev, "%s: slots=%d, slot_width=%d",
+		__func__, slots, slot_width);
+
+	if (((p_tas25xx->ch_count == 1) && (slots < 1)) ||
+		((p_tas25xx->ch_count == 2) && (slots < 2))) {
+		dev_err(plat_data->dev, "Invalid Slots %d\n", slots);
+		return ret;
+	}
+	p_tas25xx->mn_slots = slots;
+
+	if ((slot_width != 16) &&
+		(slot_width != 24) &&
+		(slot_width != 32)) {
+		dev_err(plat_data->dev, "Unsupported slot width %d\n", slot_width);
+		return ret;
+	}
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		ret = tas25xx_rx_set_bitwidth(p_tas25xx, slot_width, i);
+		if (ret) {
+			dev_err(plat_data->dev, "Error =%d while setting bitwidth, ch=%d,",
+				ret, i);
+		}
+	}
+
+	return ret;
+}
+
+/* tas25xx_set_tdm_tx_slot function is redesigned to accomodate change in
+ * tas25xx_iv_vbat_slot_config()
+ */
+int tas25xx_set_tdm_tx_slot(struct tas25xx_priv *p_tas25xx,
+	int slots, int slot_width)
+{
+	int ret = -1;
+
+	if ((slot_width != 16) &&
+		(slot_width != 24) &&
+		(slot_width != 32)) {
+		pr_err("Unsupported slot width %d\n", slot_width);
+		return ret;
+	}
+
+	if (((p_tas25xx->ch_count == 1) && (slots < 2)) ||
+		((p_tas25xx->ch_count == 2) && (slots < 4))) {
+		pr_err("Invalid Slots %d\n", slots);
+		return ret;
+	}
+	p_tas25xx->mn_slots = slots;
+
+	ret = tas25xx_iv_vbat_slot_config(p_tas25xx, slot_width);
+
+	return ret;
+}

+ 497 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/src/tas25xx-misc.c

@@ -0,0 +1,497 @@
+/*
+ * =============================================================================
+ * Copyright (c) 2016  Texas Instruments Inc.
+ *
+ * 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 the Free Software
+ * Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * File:
+ *	tas25xx-misc.c
+ *
+ * Description:
+ *	misc driver for Texas Instruments
+ *	TAS25XX High Performance 4W Smart Amplifier
+ *
+ * =============================================================================
+ */
+
+#define DEBUG
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/fcntl.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+
+#include "../inc/tas25xx.h"
+#include "../inc/tas25xx-misc.h"
+#include <linux/dma-mapping.h>
+
+#define FMT "%02x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n"
+#define MAX_FMT_COUNT 512
+
+enum tas_devop_t {
+	OP_REG_READ = 0,
+	OP_PAGE_READ = 1,
+	OP_SNG_WRITE = 2,
+	OP_BURST_WRITE = 3,
+};
+
+struct tas_audio_dev {
+	uint8_t channel;
+	uint8_t book;
+	uint8_t page;
+	uint8_t reg;
+	uint8_t read_pending;
+	enum tas_devop_t op;
+};
+
+
+static struct tas_audio_dev s_tasdevop;
+static uint32_t s_r[128];
+static struct tas25xx_priv *g_tas25xx;
+
+static int32_t tas25xx_file_open(struct inode *inode, struct file *file)
+{
+	struct tas25xx_priv *p_tas25xx = g_tas25xx;
+
+	file->private_data = (void *)p_tas25xx;
+
+	pr_info("TAS25XX %s\n", __func__);
+	return 0;
+}
+
+static int32_t tas25xx_file_release(struct inode *inode, struct file *file)
+{
+	pr_info("TAS25XX %s\n", __func__);
+
+	file->private_data = (void *)NULL;
+
+	return 0;
+}
+
+static ssize_t tas25xx_file_read(struct file *file,
+	char *buf, size_t count, loff_t *ppos)
+{
+	struct tas25xx_priv *p_tas25xx =
+		(struct tas25xx_priv *)file->private_data;
+	int32_t ret = 0, i, count_l;
+	uint8_t  *p_kbuf = NULL;
+	uint32_t  reg = 0;
+	uint32_t  len = 0;
+	uint32_t  value = 0;
+	uint32_t  channel = 0;
+
+	mutex_lock(&p_tas25xx->file_lock);
+
+	pr_info("%s size=%zu", __func__, count);
+
+	if (count > 8) {
+		uint8_t *ref_ptr;
+		if (!s_tasdevop.read_pending) {
+			count = 0;
+			goto done_read;
+		}
+
+		p_kbuf = kzalloc(MAX_FMT_COUNT, GFP_KERNEL);
+		if (p_kbuf == NULL)
+			goto done_read;
+
+		ref_ptr = p_kbuf;
+
+		channel = s_tasdevop.channel;
+		pr_info("%s ch=%d B:P %02x:%02x\n",
+			__func__, channel, s_tasdevop.book, s_tasdevop.page);
+
+		switch (s_tasdevop.op) {
+		case OP_REG_READ:
+			reg = TAS25XX_REG(s_tasdevop.book, s_tasdevop.page, s_tasdevop.reg);
+			ret = p_tas25xx->read(p_tas25xx, channel,
+				reg, &value);
+			if (ret < 0)
+				count = snprintf(ref_ptr, 64, "%s\n", "Error");
+			else
+				count = snprintf(ref_ptr, 64, "%02x\n", value);
+
+			ret = copy_to_user(buf, ref_ptr, count);
+			if (ret) {
+				pr_err("TAS25XX:%d reg read, copy to user buf ret= %d", __LINE__, ret);
+				count = ret;
+			} else {
+				pr_info("TAS25XX: %s ch=%d B:P:R %02x:%02x:%02x(%d) value=%d\n",
+					__func__, channel, s_tasdevop.book, s_tasdevop.page,
+					s_tasdevop.reg, reg, value);
+			}
+			break;
+
+		case OP_PAGE_READ:
+			reg = TAS25XX_REG(s_tasdevop.book, s_tasdevop.page, 0);
+			for (i = 0; i < 128; i++) {
+				ret = p_tas25xx->read(p_tas25xx, channel,
+					reg + i, &s_r[i]);
+				if (ret) {
+					memset(s_r, 0, sizeof(s_r));
+					break;
+				}
+			}
+			p_tas25xx->read(p_tas25xx, channel, reg, &s_r[0]);
+
+			if (ret) {
+				count = snprintf(ref_ptr, 64, "error=%d\n", ret);
+			} else {
+				count_l = 0;
+				if (count < ((52*8)+1)) {
+					count = snprintf(ref_ptr, 64,
+						"page dump not possible\n");
+				} else {
+					for (i = 0; i < 8; i++) {
+						count_l += snprintf(ref_ptr, 64, FMT, i, s_r[(i*16) + 0],
+							s_r[(i*16) + 1], s_r[(i*16) + 2], s_r[(i*16) + 3], s_r[(i*16) + 4],
+							s_r[(i*16) + 5], s_r[(i*16) + 6], s_r[(i*16) + 7], s_r[(i*16) + 8],
+							s_r[(i*16) + 9], s_r[(i*16) + 10], s_r[(i*16) + 11], s_r[(i*16) + 12],
+							s_r[(i*16) + 13], s_r[(i*16) + 14], s_r[(i*16) + 15]);
+						ref_ptr += 52;
+					}
+					count = count_l;
+				}
+			}
+
+			ret = copy_to_user(buf, p_kbuf, count);
+			if (ret) {
+				pr_err("TAS25XX:%s page read, copy buffer failed.\n", __func__);
+				count = ret;
+			}
+			break;
+
+		default:
+			count = snprintf(p_kbuf, 64, "%s\n", "invalid op");
+			ret = copy_to_user(buf, p_kbuf, count);
+			if (ret) {
+				pr_err("TAS25XX:%s invalid op, copy buffer failed.\n", __func__);
+				count = ret;
+			}
+			break;
+		}
+
+		s_tasdevop.read_pending = 0;
+		goto done_read;
+	} else if (count == 7) {
+
+	p_kbuf = kzalloc(count, GFP_KERNEL);
+	if (p_kbuf == NULL)
+		goto done_read;
+
+	ret = copy_from_user(p_kbuf, buf, count);
+	if (ret != 0) {
+		pr_err("TAS25XX copy_from_user failed.\n");
+			count = ret;
+		goto done_read;
+	}
+
+	if (p_kbuf[1] >= p_tas25xx->ch_count)
+		goto done_read;
+
+	channel = p_kbuf[1];
+
+	switch (p_kbuf[0]) {
+	case TIAUDIO_CMD_REG_READ:
+	{
+		reg = ((uint32_t)p_kbuf[2] << 24) +
+			((uint32_t)p_kbuf[3] << 16) +
+			((uint32_t)p_kbuf[4] << 8) +
+			(uint32_t)p_kbuf[5];
+
+		pr_info("TAS25XX TIAUDIO_CMD_REG_READ: current_reg = 0x%x, count=%d\n",
+			reg, (int)count-6);
+		len = count-6;
+		if (len == 1) {
+			value = 0;
+
+			ret = p_tas25xx->read(p_tas25xx, channel,
+				reg, &value);
+			if (ret < 0) {
+				pr_err("TAS25XX dev read fail %d\n", ret);
+				break;
+			}
+			p_kbuf[6] = value;
+			ret = copy_to_user(buf, p_kbuf, count);
+				if (ret) {
+					count = ret;
+					pr_err("TAS25XX TIAUDIO_CMD_REG_READ copy to user %d\n", ret);
+				}
+		} else if (len > 1) {
+			ret = p_tas25xx->bulk_read(p_tas25xx, channel,
+				reg, (uint8_t  *)&p_kbuf[6], len);
+			if (ret < 0) {
+				pr_err("TAS25XX dev bulk read fail %d\n", ret);
+			} else {
+				ret = copy_to_user(buf, p_kbuf, count);
+					if (ret) {
+						count = ret;
+						pr_err("TAS25XX TIAUDIO_CMD_REG_READ copy to user fail %d\n",
+						ret);
+			}
+		}
+	}
+		}
+	break;
+	}
+	} else {
+		pr_err("TAS25XX:%d invalid size %zu", __LINE__, count);
+		goto done_read;
+	}
+
+done_read:
+	kfree(p_kbuf);
+	mutex_unlock(&p_tas25xx->file_lock);
+	return count;
+}
+
+static int32_t handle_read_write(struct tas25xx_priv *p_tas25xx,
+	int32_t read_write_op, int32_t count, uint8_t *buf)
+{
+	static uint8_t ch_bpr[8] = {0};
+	int32_t val, buf_sz, i;
+	int32_t ret = 0;
+	int32_t reg;
+	int8_t l_buf[3];
+
+	buf_sz = count - 2;
+	buf += 2;
+
+	i = 0;
+	l_buf[2] = 0;
+	while (buf_sz >= 2) {
+		memcpy(l_buf, buf, 2);
+		ret = kstrtoint(l_buf, 16, &val);
+		if (ret) {
+			pr_err("TAS25XX:%d Parsing err for %s, err=%d", __LINE__, l_buf, ret);
+			break;
+		}
+		if (i <= 7) {
+			pr_info("tas25xx: %s i=%d, val=%d\n", __func__, i, val);
+			ch_bpr[i] = (uint8_t)val;
+		} else {
+			pr_info("tas25xx: write supported only for 4 bytes,ignoring additional bytes\n");
+			break;
+		}
+		buf += 3;
+		buf_sz -= 3;
+		i++;
+	}
+
+	if (ret)
+		goto read_write_done;
+
+	pr_info("tas25xx: ch=%d, BPR %02x:%02x:%02x v=%d %d %d %d(cnt=%d)\n",
+		ch_bpr[0], ch_bpr[1], ch_bpr[2], ch_bpr[3],
+		ch_bpr[4], ch_bpr[5], ch_bpr[6], ch_bpr[7], i);
+
+	if (ch_bpr[0] >= p_tas25xx->ch_count) {
+		ret = -EINVAL;
+		goto read_write_done;
+	}
+
+	s_tasdevop.channel = ch_bpr[0];
+	s_tasdevop.book = ch_bpr[1];
+	s_tasdevop.page = ch_bpr[2];
+	s_tasdevop.reg = ch_bpr[3];
+	if (read_write_op == 1)
+		s_tasdevop.read_pending = 1;
+	else
+		s_tasdevop.read_pending = 0;
+
+	if (read_write_op == 1) {
+		if (i == 3) {
+			pr_info("tas25xx: page read\n");
+			s_tasdevop.op = OP_PAGE_READ;
+		} else if (i == 4) {
+			pr_info("tas25xx: single read\n");
+			s_tasdevop.op = OP_REG_READ;
+		} else {
+			pr_info("tas25xx: page/single read is supported\n");
+			s_tasdevop.read_pending = 0;
+			ret = -EINVAL;
+		}
+	} else if (read_write_op == 2) {
+		if (i == 5) {
+			pr_info("tas25xx: single write\n");
+			s_tasdevop.op = OP_SNG_WRITE;
+		} else if (i == 8) {
+			pr_info("tas25xx: burst write\n");
+			s_tasdevop.op = OP_BURST_WRITE;
+		} else {
+			pr_info("tas25xx: signle/burst write is supported\n");
+			ret = -EINVAL;
+		}
+	} else {
+		pr_info("tas25xx: Only read and write is supported\n");
+		ret = -EINVAL;
+	}
+
+	if (read_write_op == 2) {
+		val = ch_bpr[4];
+		reg = TAS25XX_REG(s_tasdevop.book, s_tasdevop.page, s_tasdevop.reg);
+		if (s_tasdevop.op == OP_SNG_WRITE) {
+			ret = p_tas25xx->write(p_tas25xx,
+					s_tasdevop.channel, reg, val);
+		} else if (s_tasdevop.op == OP_BURST_WRITE) {
+			ret = p_tas25xx->bulk_write(p_tas25xx,
+				s_tasdevop.channel, reg, &ch_bpr[4], 4);
+		}
+	}
+
+read_write_done:
+	if (ret < 0)
+		count = ret;
+
+	return count;
+}
+
+static ssize_t tas25xx_file_write(struct file *file,
+	const char *buf, size_t count, loff_t *ppos)
+{
+	struct tas25xx_priv *p_tas25xx =
+		(struct tas25xx_priv *)file->private_data;
+	int32_t ret = 0;
+	uint8_t  *p_kbuf = NULL;
+	uint32_t  reg = 0;
+	uint32_t  len = 0;
+	uint32_t  channel = 0;
+	int32_t read_write_op;
+
+	mutex_lock(&p_tas25xx->file_lock);
+
+	pr_info("%s size=%zu", __func__, count);
+
+	if (count < 7) {
+		pr_err("TAS25XX invalid size %zu\n", count);
+		ret = -EINVAL;
+		goto done_write;
+	}
+
+	p_kbuf = kzalloc(count, GFP_KERNEL);
+	if (p_kbuf == NULL) {
+		ret = -ENOMEM;
+		goto done_write;
+	}
+
+	ret = copy_from_user(p_kbuf, buf, count);
+	if (ret != 0) {
+		count = ret;
+		pr_err("TAS25XX copy_from_user failed.\n");
+		goto done_write;
+	}
+
+	if ((p_kbuf[0] == 'w' || p_kbuf[0] == 'W') && p_kbuf[1] == ' ')
+		read_write_op = 2;
+	else if ((p_kbuf[0] == 'r' || p_kbuf[0] == 'R') && p_kbuf[1] == ' ')
+		read_write_op = 1;
+	else
+		read_write_op = 0;
+
+	if (read_write_op) {
+		count = handle_read_write(p_tas25xx, read_write_op, count, p_kbuf);
+		goto done_write;
+	}
+
+	if (p_kbuf[1] >= p_tas25xx->ch_count) {
+		pr_err("TAS25XX: channel count exceeds actual chanel count\n");
+		goto done_write;
+	}
+	channel = p_kbuf[1];
+
+	switch (p_kbuf[0]) {
+	case TIAUDIO_CMD_REG_WITE:
+		if (count > 5) {
+			reg = ((uint32_t)p_kbuf[2] << 24) +
+				((uint32_t)p_kbuf[3] << 16) +
+				((uint32_t)p_kbuf[4] << 8) +
+				(uint32_t)p_kbuf[5];
+			len = count - 6;
+			pr_info("TAS25XX TIAUDIO_CMD_REG_WITE, Reg=0x%x, Val=0x%x\n",
+				reg, p_kbuf[6]);
+			if (len == 1) {
+				uint32_t  value = 0;
+
+				value = p_kbuf[6];
+				ret = p_tas25xx->write(p_tas25xx, channel, reg,
+					value);
+			} else if (len > 1) {
+				ret = p_tas25xx->bulk_write(p_tas25xx, channel,
+					reg, &p_kbuf[6], len);
+			}
+		} else {
+			pr_err("TAS25XX %s, write len fail, count=%d.\n",
+				__func__, (int)count);
+		}
+	break;
+	}
+
+done_write:
+	kfree(p_kbuf);
+	mutex_unlock(&p_tas25xx->file_lock);
+
+	return count;
+}
+
+static const struct file_operations fops = {
+	.owner = THIS_MODULE,
+	.read = tas25xx_file_read,
+	.write = tas25xx_file_write,
+	.unlocked_ioctl = NULL,
+	.open = tas25xx_file_open,
+	.release = tas25xx_file_release,
+};
+
+#define MODULE_NAME	"tas_audio_dev"
+static struct miscdevice tas25xx_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = MODULE_NAME,
+	.fops = &fops,
+};
+
+int32_t tas25xx_register_misc(struct tas25xx_priv *p_tas25xx)
+{
+	int32_t ret = 0;
+
+	g_tas25xx = p_tas25xx;
+	ret = misc_register(&tas25xx_misc);
+	if (ret)
+		pr_err("TI-SmartPA TAS25XX misc fail: %d\n", ret);
+
+	pr_info("TI-SmartPA %s, leave\n", __func__);
+
+	return ret;
+}
+EXPORT_SYMBOL(tas25xx_register_misc);
+
+int32_t tas25xx_deregister_misc(struct tas25xx_priv *p_tas25xx)
+{
+	misc_deregister(&tas25xx_misc);
+	g_tas25xx = NULL;
+	return 0;
+}
+EXPORT_SYMBOL(tas25xx_deregister_misc);
+
+/*
+ * MODULE_AUTHOR("Texas Instruments Inc.");
+ * MODULE_DESCRIPTION("TAS25XX Misc Smart Amplifier driver");
+ * MODULE_LICENSE("GPL v2");
+ */

+ 2495 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/src/tas25xx-regbin-parser.c

@@ -0,0 +1,2495 @@
+/*
+ * ALSA SoC Texas Instruments TAS25XX High Performance 4W Smart Amplifier
+ *
+ * Copyright (C) 2022 Texas Instruments, Inc.
+ *
+ * Author: Niranjan H Y, Vijeth P O
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <linux/firmware.h>
+#include <linux/wait.h>
+#include <linux/i2c.h>
+#include "../inc/tas25xx.h"
+#include "../inc/tas25xx-regmap.h"
+#include "../inc/tas25xx-regbin-parser.h"
+#include "../inc/tas25xx-device.h"
+
+#define TAS25XX_BINFILE_NAME	"tismartpa_driver_tuning.bin"
+
+#define MDATA_HDR_SZ 9
+#define ONE_BIN_MD_SZ 20
+#define HDR_STR_SZ	5
+#define MAIN_BLOCK_SIZE 5
+#define HDR_N_ITS_SZ 9
+#define ANY_CHANNEL 0xffffffff
+#define MDATA "MDATA"
+#define HEADER "HEADR"
+#define INITP_STR "INITP"
+#define INTRP_STR  "INTRP"
+#define HW_PRMS_STR "HWPRM"
+#define PROFP_STR   "PROFP"
+#define KCNTRL_STR  "KCNTR"
+#define BLK_OP_STR  "BLKOP"
+#define ALGOP_STR   "ALGOP"
+
+enum block_types_t {
+	BLK_SW_RST,
+	BLK_POWER_CHECK,
+	BLK_MUTE,
+	BLK_CAL_INIT,
+	BLK_CAL_DEINIT,
+	BLK_RX_FMT,
+	BLK_TX_FMT,
+	BLK_INVALID,
+};
+
+const char *SampleRate[3] = { "48000", "44100", "96000"};
+const char *FMT_INV[4] = { "NB_NF", "IB_NF", "NB_IF", "IB_IF" };
+const char *FMT_MASK[4] = { "I2S", "DSP_A", "DSP_B", "LEFT_J"};
+const char *RX_SLOTS[3] = { "16", "24", "32" };
+const char *TX_SLOTS[3] = { "16", "24", "32" };
+const char *RX_BITWIDTH[3] = { "16", "24", "32" };
+const char *RX_SLOTLEN[3] = { "16", "24", "32" };
+const char *TX_SLOTLEN[3] = { "16", "24", "32" };
+const char *CMD_ID[4] = { "CMD_SINGLE_WRITE", "CMD_BURST_WRITES", "CMD_UPDATE_BITS", "CMD_DELAY"};
+
+struct hw_params_t {
+	char *SampleRate[3];
+	char *FMT_INV[4];
+	char *FMT_MASK[4];
+	char *RX_SLOTS[3];
+	char *TX_SLOTS[3];
+	char *RX_BITWIDTH[3];
+	char *RX_SLOTLEN[3];
+	char *TX_SLOTLEN[3];
+};
+
+struct regbin_parser {
+	struct bin_header head;
+	struct default_hw_params def_hw_params;
+	char *init_params[MAX_CHANNELS];
+	struct hw_params_t hw_params[MAX_CHANNELS];
+};
+
+static struct regbin_parser s_rbin;
+
+static uint32_t g_no_of_profiles;
+static int32_t g_tas25xx_profile;
+
+struct tas25xx_profiles {
+	uint8_t name[64];
+	uint32_t misc_control;
+	uint8_t *pre_power_up;
+	uint8_t *post_power_up;
+	uint8_t *pre_power_down;
+	uint8_t *post_power_down;
+};
+
+struct tas25xx_profiles_channel {
+	struct tas25xx_profiles *tas_profiles;
+};
+
+static struct tas25xx_profiles_channel g_profile_data_list[MAX_CHANNELS];
+
+static char **g_profile_list;
+static struct soc_enum tas25xx_switch_enum;
+static struct snd_kcontrol_new tas25xx_profile_ctrl;
+
+
+struct tas25xx_kcontrol_int {
+	char *name;
+	char channel;
+	int32_t reg;
+	char reg_type;
+	int32_t mask;
+	int32_t range_min;
+	int32_t range_max;
+	int32_t step;
+	int32_t def;
+	int32_t count;
+	int32_t curr_val;
+	uint32_t misc_info; /* additional info regarding kcontrol */
+	char *chardata;
+	int32_t *intdata;
+	struct soc_mixer_control mctl;
+};
+
+struct tas25xx_kcontrol_enum_data {
+	char name[64];
+	char *data;
+};
+
+struct tas25xx_kcontrol_enum {
+	char *name;
+	char channel;
+	char def;
+	int32_t count;
+	int32_t curr_val;
+	uint32_t misc_info; /* additional info regarding kcontrol */
+	struct tas25xx_kcontrol_enum_data *data;
+	char **enum_texts;
+	struct soc_enum tas25xx_kcontrol_enum;
+};
+
+union tas25xx_kcontrols_types {
+	struct tas25xx_kcontrol_int int_type;
+	struct tas25xx_kcontrol_enum enum_type;
+};
+
+struct tas25xx_kcontrols {
+	char type;
+	union tas25xx_kcontrols_types kcontrol;
+};
+
+static int32_t g_no_of_kcontrols;
+/* bin file parsed data */
+static struct tas25xx_kcontrols *g_kctrl_data;
+/* creating kcontrols */
+static struct snd_kcontrol_new *g_kctrl_ctrl;
+static struct tas25xx_priv *g_tas25xx;
+
+static uint8_t *tas25xx_read_size_bytes(uint8_t *in, uint8_t **out);
+static uint32_t get_block_size_noadvance(uint8_t *mem_in);
+
+#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
+void tas25xx_parse_algo_bin(int ch_count, u8 *buf);
+#endif /* CONFIG_TAS25XX_ALGO */
+
+int32_t change_endian(void *data, int32_t size)
+{
+	int32_t i = 0;
+	int32_t j = 0;
+	int32_t *in;
+	char *data_l;
+	char c;
+
+	if (size%4 != 0) {
+		pr_err("tas25xx: %s size %d are not 4bytes aligned!!!",
+			__func__, size);
+	} else {
+		in = (int32_t *)data;
+		c = 0;
+		for (i = 0; i < size/4; i++) {
+			data_l = (char *)&in[i];
+			for (j = 0; j < 2; j++) {
+				c = data_l[3-j];
+				data_l[3-j] = data_l[j];
+				data_l[j] = c;
+			}
+		}
+	}
+	return 0;
+}
+
+static bool header_check(struct tas25xx_priv *p_tas25xx,
+	const uint8_t *s1, const uint8_t *s2)
+{
+	bool success = false;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = p_tas25xx->platform_data;
+
+	if (memcmp(s1, s2, HDR_STR_SZ) == 0)
+		success = true;
+
+	return success;
+}
+
+static int8_t *find_block_for_channel(struct tas25xx_priv *p_tas25xx,
+	uint8_t *inp, uint8_t *blk_name, uint32_t ch)
+{
+	int32_t sz;
+	uint8_t *outp = NULL;
+	uint8_t *buf;
+	uint32_t count = -1;
+	uint32_t any_channel = 0;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = p_tas25xx->platform_data;
+
+	/* header 5 bytes + size 4 bytes */
+	if (inp <= (p_tas25xx->fw_data + p_tas25xx->fw_size - HDR_N_ITS_SZ))
+		if (inp && header_check(p_tas25xx, inp, blk_name))
+			return inp;
+
+	/* start from begginging */
+	buf = p_tas25xx->fw_data;
+
+	if (ch == ANY_CHANNEL)
+		any_channel = 1;
+
+	while (buf <= (p_tas25xx->fw_data + p_tas25xx->fw_size - HDR_N_ITS_SZ)) {
+		if (header_check(p_tas25xx, buf, INITP_STR)) {
+			dev_info(plat_data->dev,
+				"block %s found, incrementing count", INITP_STR);
+			count++;
+		} else {
+			dev_info(plat_data->dev,
+				"block check, found %.5s(@%p) count=%d", buf, buf, count);
+		}
+		if (header_check(p_tas25xx, buf, blk_name)) {
+			if (any_channel || (count == ch)) {
+				outp = buf;
+				break;
+			}
+		}
+
+		buf += 5; /* header */
+		sz = get_block_size_noadvance(buf);
+		buf += 4;
+		buf += sz;
+	}
+
+	if (outp) {
+		dev_warn(plat_data->dev,
+			"found block %s @%p, ch=%d", blk_name, outp, count);
+	} else {
+		dev_err(plat_data->dev, "block %s not found", blk_name);
+	}
+
+	return outp;
+}
+
+/* For profile KControls */
+static int32_t tas25xx_profile_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	if (!ucontrol) {
+		pr_err("tas25xx: %s:ucontrol is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	ucontrol->value.integer.value[0] = g_tas25xx_profile;
+
+	return 0;
+}
+
+static int32_t tas25xx_profile_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int32_t temp, ret = -EINVAL;
+
+	if (!ucontrol)
+		return ret;
+
+	temp = ucontrol->value.integer.value[0];
+	if (temp >= 0 && temp < g_no_of_profiles) {
+		g_tas25xx_profile = temp;
+	pr_info("tas25xx: setting profile %d", g_tas25xx_profile);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int32_t tas25xx_create_profile_controls(struct tas25xx_priv *p_tas25xx)
+{
+	struct tas25xx_profiles *tas_profiles_t = (struct tas25xx_profiles *)g_profile_data_list[0].tas_profiles;
+	struct linux_platform *plat_data =
+		(struct linux_platform *)p_tas25xx->platform_data;
+	int32_t ret = 0;
+	int32_t i = 0;
+
+	g_profile_list = kzalloc(g_no_of_profiles * sizeof(char *), GFP_KERNEL);
+	if (!g_profile_list) {
+		ret = -ENOMEM;
+		goto EXIT;
+	}
+
+	for (i = 0; i < g_no_of_profiles; i++) {
+		g_profile_list[i] = kzalloc(64, GFP_KERNEL);
+		if (!g_profile_list[i]) {
+			ret = -ENOMEM;
+			goto EXIT;
+		}
+		memcpy(g_profile_list[i], tas_profiles_t[i].name, 64);
+	}
+
+	tas25xx_switch_enum.items = g_no_of_profiles;
+	tas25xx_switch_enum.texts = (const char * const *)g_profile_list;
+
+	tas25xx_profile_ctrl.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	tas25xx_profile_ctrl.name = "TAS25XX CODEC PROFILE";
+	tas25xx_profile_ctrl.info = snd_soc_info_enum_double;
+	tas25xx_profile_ctrl.get = tas25xx_profile_get;
+	tas25xx_profile_ctrl.put = tas25xx_profile_put;
+	tas25xx_profile_ctrl.private_value = (unsigned long)(&tas25xx_switch_enum);
+
+	ret = snd_soc_add_component_controls(plat_data->codec,
+			&tas25xx_profile_ctrl, 1);
+EXIT:
+	return ret;
+}
+
+
+static uint8_t *process_block_get_cmd(uint8_t *mem_in, int8_t *cmd)
+{
+	*cmd = *mem_in;
+	mem_in++;
+	return mem_in;
+}
+
+static uint32_t get_block_size_noadvance(uint8_t *mem_in)
+{
+	int32_t sz;
+	int32_t *ptr = (int32_t *)mem_in;
+
+	sz = *ptr;
+	ptr++;
+	return sz;
+}
+
+static uint8_t *process_block_get_single_write_data(uint8_t *mem_in, int32_t *reg, uint8_t *val)
+{
+	uint8_t *data8b;
+	int32_t *data32b;
+
+	data32b = (int32_t *)mem_in;
+	*reg = *data32b;
+	data32b++;
+
+	data8b = (uint8_t *)data32b;
+	*val = *data8b;
+	data8b++;
+
+	return data8b;
+}
+
+static uint8_t *process_block_get_burst_write_data(uint8_t *mem_in,
+	int32_t *reg, int32_t *count, uint8_t **bulk_buffer)
+{
+	uint8_t *data8b;
+	int32_t *data32b;
+
+	data32b = (int32_t *)mem_in;
+	*reg = *data32b;
+	data32b++;
+
+	*count = *data32b;
+	data32b++;
+
+	data8b = (uint8_t *)data32b;
+	*bulk_buffer = data8b;
+
+	data8b = data8b + (*count);
+
+	return data8b;
+}
+
+static uint8_t *process_block_get_bit_update_data(uint8_t *mem_in,
+	int32_t *reg, uint8_t *mask, uint8_t *value)
+{
+	uint8_t *data8b;
+	int32_t *data32b;
+
+	data32b = (int32_t *)mem_in;
+	*reg = *data32b;
+	data32b++;
+
+	data8b = (uint8_t *)data32b;
+	*mask = *data8b;
+	data8b++;
+	*value = *data8b;
+	data8b++;
+
+	return data8b;
+}
+
+static uint8_t *process_block_get_delay_data(uint8_t *mem_in, int32_t *delay)
+{
+	uint8_t *data8b;
+	int32_t *data32b;
+
+	data32b = (int32_t *)mem_in;
+	*delay = *data32b;
+	data32b++;
+
+	data8b = (uint8_t *)data32b;
+
+	return data8b;
+}
+
+int32_t tas25xx_process_block(struct tas25xx_priv *p_tas25xx, char *mem, int32_t chn)
+{
+	int32_t i = 0;
+	int32_t block_size = 0;
+	struct linux_platform *plat_data = NULL;
+	int32_t ret = 0;
+	int32_t ret_i = 0;
+	int32_t reg;
+	int32_t count;
+	int32_t delay;
+	int fw_state;
+	int8_t cmd;
+	uint8_t mask;
+	uint8_t val;
+	uint8_t *buffer = NULL;
+	uint8_t *ptr = NULL;
+
+	fw_state = atomic_read(&p_tas25xx->fw_state);
+	if (fw_state != TAS25XX_DSP_FW_OK)
+		return -EINVAL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+	memcpy(&block_size, mem, sizeof(int32_t));
+	mem += sizeof(int32_t);
+	ptr = mem;
+
+	while (i < block_size) {
+		ptr = process_block_get_cmd(ptr, &cmd);
+		switch (cmd) {
+		case CMD_SINGLE_WRITE:
+			ptr = process_block_get_single_write_data(ptr, &reg, &val);
+			i += CMD_SINGLE_WRITE_SZ;
+			ret_i = p_tas25xx->write(p_tas25xx, chn, reg, val);
+			dev_info(plat_data->dev, "ch=%d Cmd = %s B:P:R %02x:%02x:%02x, value=%02x, ret=%d\n",
+				chn, CMD_ID[cmd], TAS25XX_BOOK_ID(reg), TAS25XX_PAGE_ID(reg),
+				TAS25XX_PAGE_REG(reg), val, ret_i);
+			ret |= ret_i;
+			break;
+
+		case CMD_BURST_WRITES:
+			ptr = process_block_get_burst_write_data(ptr, &reg, &count, &buffer);
+			i += CMD_BURST_WRITES_SZ + count;
+			ret_i = p_tas25xx->bulk_write(p_tas25xx, chn, reg, buffer, count);
+			dev_info(plat_data->dev,
+				"ch=%d Cmd = %s B:P:R %02x:%02x:%02x, count=%d, buf=%02x %02x %02x %02x ret=%d\n",
+				chn, CMD_ID[cmd], TAS25XX_BOOK_ID(reg), TAS25XX_PAGE_ID(reg), TAS25XX_PAGE_REG(reg),
+				count, buffer[0], buffer[1], buffer[2], buffer[3], ret_i);
+			ret |= ret_i;
+			break;
+
+		case CMD_UPDATE_BITS:
+			ptr = process_block_get_bit_update_data(ptr, &reg, &mask, &val);
+			i += CMD_UPDATE_BITS_SZ;
+			ret_i = p_tas25xx->update_bits(p_tas25xx, chn, reg, mask, val);
+			dev_info(plat_data->dev,
+				"ch=%d Cmd = %s B:P:R %02x:%02x:%02x mask=%02x, val=%02x, ret=%d",
+				chn, CMD_ID[cmd], TAS25XX_BOOK_ID(reg), TAS25XX_PAGE_ID(reg), TAS25XX_PAGE_REG(reg),
+				mask, val, ret_i);
+			ret |= ret_i;
+			break;
+
+		case CMD_DELAY:
+			ptr = process_block_get_delay_data(ptr, &delay);
+			i += CMD_DELAY_SZ;
+			dev_info(plat_data->dev, "ch=%d Cmd = %s delay=%x(%d)\n",
+				chn, CMD_ID[cmd], delay, delay);
+			ret |= 0;
+			/*delay in ms, convert to us*/
+			usleep_range(delay * 1000, (delay * 1000) + 100);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int32_t tas25xx_check_if_powered_on(struct tas25xx_priv *p_tas25xx, int *state, int chn)
+{
+	int8_t cmd;
+	uint8_t expected_val = 0;
+	uint8_t mask;
+	uint32_t val;
+	int32_t reg;
+	int32_t i = 0;
+	int32_t block_size = 0;
+	struct linux_platform *plat_data = NULL;
+	int32_t ret = 0;
+	uint8_t *ptr = p_tas25xx->block_op_data[chn].power_check;
+	bool expected = true;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+	if (!ptr) {
+		dev_info(plat_data->dev, "%s null buffer recieved for ch=%d",
+			__func__, chn);
+		return -EINVAL;
+	}
+
+	memcpy(&block_size, ptr, sizeof(int32_t));
+	ptr += sizeof(int32_t);
+
+	while (i < block_size) {
+		ptr = process_block_get_cmd(ptr, &cmd);
+		switch (cmd) {
+		case CMD_SINGLE_WRITE:
+			ptr = process_block_get_single_write_data(ptr, &reg, &expected_val);
+			i += CMD_SINGLE_WRITE_SZ;
+			ret = p_tas25xx->read(p_tas25xx, chn, reg, &val);
+			dev_info(plat_data->dev,
+				"Chn=%d Cmd = %s reg=%x(%d), value read=%02x, expected=%02x\n",
+				chn, CMD_ID[cmd], reg, reg, val, expected_val);
+			if ((val & 0xFFFF) != expected_val)
+				expected = expected && false;
+			break;
+
+		case CMD_UPDATE_BITS:
+			ptr = process_block_get_bit_update_data(ptr, &reg, &mask, &expected_val);
+			i += CMD_UPDATE_BITS_SZ;
+			dev_info(plat_data->dev, "Cmd = %s reg=%x(%d), mask=%02x, expected_val=%02x\n",
+				CMD_ID[cmd], reg, reg, mask, expected_val);
+			ret = p_tas25xx->read(p_tas25xx, chn, reg, &val);
+			dev_info(plat_data->dev,
+				"Chn=%d Cmd = %s reg=%x(%d), value read=%02x, expected=%02x\n",
+				chn, CMD_ID[cmd], reg, reg, val, expected_val);
+			if ((val & 0xFFFF) != expected_val)
+				expected = expected && false;
+			break;
+
+		default:
+			dev_info(plat_data->dev, "Chn=%d default cmd=%d", chn, cmd);
+			break;
+		}
+	}
+
+	if (expected)
+		*state = 1;
+	else
+		*state = 0;
+
+	return ret;
+}
+
+static int32_t tas25xx_parse_init_params(struct tas25xx_priv *p_tas25xx, uint8_t **buf, int32_t ch)
+{
+	int32_t ret = 0;
+	int32_t size = 0;
+	struct linux_platform *plat_data = NULL;
+	uint8_t *data = *buf;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	data = find_block_for_channel(p_tas25xx, data, INITP_STR, ch);
+	if (!data)
+		return -EINVAL;
+
+	data += HDR_STR_SZ;
+
+	/* Parsing Init Params */
+	memcpy(&size, data, sizeof(size));
+	s_rbin.init_params[ch] = data;
+	data += (size + sizeof(size));
+
+	*buf = data;
+
+	return ret;
+}
+
+static uint8_t *tas25xx_read_size_bytes(uint8_t *in, uint8_t **out)
+{
+	uint8_t *buf;
+	int32_t size = 0;
+
+	if (!in || !out)
+		return NULL;
+
+	buf = in;
+	*out = buf;
+
+	memcpy(&size, buf, sizeof(size));
+	buf += size + sizeof(int32_t);
+
+	/*buf_data = in + sizeof(int32_t);
+	dev_info(plat_data->dev, "tas25xx: dump buffer, size=%d\n", size);
+	for (i = 0; i < size; i++) {
+		dev_info(plat_data->dev, "tas25xx: buf[%d] = 0x%02x,\t", i, buf_data[i]);
+	}
+	dev_info(plat_data->dev, "\n");
+	*/
+
+	return buf;
+}
+
+static int32_t tas25xx_parse_hw_params(struct tas25xx_priv *p_tas25xx, uint8_t **inbuf, int32_t ch)
+{
+	int32_t ret = 0;
+	uint8_t *next = *inbuf;
+	uint8_t *first = *inbuf;
+	uint8_t *out = NULL;
+	int32_t size = 0;
+	int32_t no_of_hw_params = 0;
+	int count = 1;
+	int end = 0;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	next = find_block_for_channel(p_tas25xx, next, HW_PRMS_STR, ch);
+	if (!next)
+		return -EINVAL;
+
+	next += HDR_STR_SZ;
+	size = get_block_size_noadvance(next);
+	next += 4;
+	no_of_hw_params = get_block_size_noadvance(next);
+	next += 4;
+
+	dev_info(plat_data->dev, "parsing HW params, inbuf=0x%p, size=%d, no_of_hw_params=%d",
+		next, size, no_of_hw_params);
+
+	next = tas25xx_read_size_bytes(next, &out);
+	while (next) {
+		switch (count) {
+		case 1:
+			s_rbin.hw_params[ch].SampleRate[SampleRate_48000] = out;
+			break;
+
+		case 2:
+			s_rbin.hw_params[ch].SampleRate[SampleRate_44100] = out;
+			break;
+
+		case 3:
+			s_rbin.hw_params[ch].SampleRate[SampleRate_96000] = out;
+			break;
+
+		case 4:
+			/* Parsing FMT_INV_NB_NF */
+			s_rbin.hw_params[ch].FMT_INV[FMT_INV_NB_NF] = out;
+			break;
+
+		case 5:
+			/* Parsing FMT_INV_IB_NF */
+			s_rbin.hw_params[ch].FMT_INV[FMT_INV_IB_NF] = out;
+			break;
+
+		case 6:
+			/* Parsing FMT_INV_NB_IF */
+			s_rbin.hw_params[ch].FMT_INV[FMT_INV_NB_IF] = out;
+			break;
+
+		case 7:
+			/* Parsing FMT_INV_IB_IF */
+			s_rbin.hw_params[ch].FMT_INV[FMT_INV_IB_IF] = out;
+			break;
+
+		case 8:
+			/* Parsing FMT_MASK_I2S */
+			s_rbin.hw_params[ch].FMT_MASK[FMT_MASK_I2S] = out;
+			break;
+
+		case 9:
+			/* Parsing FMT_MASK_DSP_A */
+			s_rbin.hw_params[ch].FMT_MASK[FMT_MASK_DSP_A] = out;
+			break;
+
+		case 10:
+			/* Parsing FMT_MASK_DSP_B */
+			s_rbin.hw_params[ch].FMT_MASK[FMT_MASK_DSP_B] = out;
+			break;
+
+		case 11:
+			/* Parsing FMT_MASK_LEFT_J */
+			s_rbin.hw_params[ch].FMT_MASK[FMT_MASK_LEFT_J] = out;
+			break;
+
+
+		case 12:
+			/* Parsing RX_SLOTS_16 */
+			s_rbin.hw_params[ch].RX_SLOTS[RX_SLOTS_16] = out;
+			break;
+
+
+		case 13:
+			/* Parsing RX_SLOTS_24 */
+			s_rbin.hw_params[ch].RX_SLOTS[RX_SLOTS_24] = out;
+			break;
+
+
+		case 14:
+			/* Parsing RX_SLOTS_32 */
+			s_rbin.hw_params[ch].RX_SLOTS[RX_SLOTS_32] = out;
+			break;
+
+
+		case 15:
+			/* Parsing TX_SLOTS_16 */
+			s_rbin.hw_params[ch].TX_SLOTS[TX_SLOTS_16] = out;
+			break;
+
+
+		case 16:
+			/* Parsing TX_SLOTS_24 */
+			s_rbin.hw_params[ch].TX_SLOTS[TX_SLOTS_24] = out;
+			break;
+
+
+		case 17:
+			/* Parsing TX_SLOTS_32 */
+			s_rbin.hw_params[ch].TX_SLOTS[TX_SLOTS_32] = out;
+			break;
+
+
+		case 18:
+			/* Parsing RX_BITWIDTH_16 */
+			s_rbin.hw_params[ch].RX_BITWIDTH[RX_BITWIDTH_16] = out;
+			break;
+
+
+		case 19:
+			/* Parsing RX_BITWIDTH_24 */
+			s_rbin.hw_params[ch].RX_BITWIDTH[RX_BITWIDTH_24] = out;
+			break;
+
+
+		case 20:
+			/* Parsing RX_BITWIDTH_32 */
+			s_rbin.hw_params[ch].RX_BITWIDTH[RX_BITWIDTH_32] = out;
+			break;
+
+
+		case 21:
+			/* Parsing RX_SLOTLEN_16 */
+			s_rbin.hw_params[ch].RX_SLOTLEN[RX_SLOTLEN_16] = out;
+			break;
+
+
+		case 22:
+			/* Parsing RX_SLOTLEN_24 */
+			s_rbin.hw_params[ch].RX_SLOTLEN[RX_SLOTLEN_24] = out;
+			break;
+
+		case 23:
+			/* Parsing RX_SLOTLEN_32 */
+			s_rbin.hw_params[ch].RX_SLOTLEN[RX_SLOTLEN_32] = out;
+			break;
+
+		case 24:
+			/* Parsing TX_SLOTLEN_16 */
+			s_rbin.hw_params[ch].TX_SLOTLEN[TX_SLOTLEN_16] = out;
+			break;
+
+		case 25:
+			/* Parsing TX_SLOTLEN_24 */
+			s_rbin.hw_params[ch].TX_SLOTLEN[TX_SLOTLEN_24] = out;
+			break;
+
+		case 26:
+			/* Parsing TX_SLOTLEN_32 */
+			s_rbin.hw_params[ch].TX_SLOTLEN[TX_SLOTLEN_32] = out;
+			end = 1;
+			break;
+
+		default:
+			end = 1;
+			break;
+		} /*<<switch>>*/
+
+		if (!end) {
+			next = tas25xx_read_size_bytes(next, &out);
+			count++;
+		} else {
+			break;
+		}
+	} /*<<while>>*/
+
+	size = (int32_t)(next - first);
+	if (size < 0)
+		size = -1 * size;
+	*inbuf = next;
+
+	dev_info(plat_data->dev, "parsing HW params exit, next=0x%p, size=%d", next, size);
+
+	return ret;
+}
+
+int tas25xx_check_if_algo_ctrl_bypassed(int ch)
+{
+	struct tas25xx_profiles *tas_profiles_t;
+	if (ch >= s_rbin.head.channels)
+		return -EINVAL;
+
+	tas_profiles_t = (struct tas25xx_profiles *)g_profile_data_list[ch].tas_profiles;
+
+	/* bit5, 1 = algo control bypass, 0 = use algo */
+	return (tas_profiles_t[g_tas25xx_profile].misc_control & 0x10);
+}
+EXPORT_SYMBOL(tas25xx_check_if_algo_ctrl_bypassed);
+
+int tas25xx_get_drv_channel_opmode(void)
+{
+	struct tas25xx_profiles *tas_profiles_t;
+
+	tas_profiles_t = (struct tas25xx_profiles *)g_profile_data_list[0].tas_profiles;
+	return tas_profiles_t[g_tas25xx_profile].misc_control;
+}
+EXPORT_SYMBOL(tas25xx_get_drv_channel_opmode);
+
+
+static int32_t tas25xx_parse_profiles(struct tas25xx_priv *p_tas25xx, uint8_t **inbuf, int32_t ch)
+{
+	int32_t ret = 0;
+	int32_t size;
+	int32_t i = 0;
+	uint8_t *buf = *inbuf;
+	uint8_t *out = NULL;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	buf = find_block_for_channel(p_tas25xx, buf, PROFP_STR, ch);
+	if (!buf)
+		return -EINVAL;
+
+	buf += HDR_STR_SZ;
+	size = get_block_size_noadvance(buf);
+	buf += 4;
+	dev_info(plat_data->dev, "parsing profiles, total size=%d", size);
+
+	memcpy(&g_no_of_profiles, buf, 1);
+	buf++;
+
+	dev_info(plat_data->dev, "Number of profiles=%d, allocation=%u", (int)g_no_of_profiles,
+		(uint32_t)(g_no_of_profiles * sizeof(struct tas25xx_profiles)));
+
+	g_profile_data_list[ch].tas_profiles =
+		kzalloc(g_no_of_profiles*(sizeof(struct tas25xx_profiles)), GFP_KERNEL);
+	if (!g_profile_data_list[ch].tas_profiles)
+		return -ENOMEM;
+
+	for (i = 0; i < g_no_of_profiles; i++) {
+		struct tas25xx_profiles *tas_profiles_t =
+			(struct tas25xx_profiles *)g_profile_data_list[ch].tas_profiles;
+		/* Parsing Name */
+		memcpy(tas_profiles_t[i].name, buf, 64);
+		buf += 64;
+
+		/* bitmask indicating misc control for profile */
+		memcpy(&tas_profiles_t[i].misc_control, buf, sizeof(uint32_t));
+		buf += 4;
+
+		/* pre_power_up */
+		buf = tas25xx_read_size_bytes(buf, &out);
+		tas_profiles_t[i].pre_power_up = out;
+
+		/* post_power_up */
+		buf = tas25xx_read_size_bytes(buf, &out);
+		tas_profiles_t[i].post_power_up = out;
+
+		/* pre_power_down */
+		buf = tas25xx_read_size_bytes(buf, &out);
+		tas_profiles_t[i].pre_power_down = out;
+
+		/* post_power_down */
+		buf = tas25xx_read_size_bytes(buf, &out);
+		tas_profiles_t[i].post_power_down = out;
+	}
+
+	*inbuf = buf;
+	return ret;
+}
+
+void tas25xx_prep_dev_for_calib(int start)
+{
+	int i, ret;
+	uint8_t *mem;
+	struct linux_platform *plat_data = NULL;
+
+	if (unlikely(!g_tas25xx))
+		return;
+
+	plat_data = (struct linux_platform *) g_tas25xx->platform_data;
+
+	for (i = 0; i < g_tas25xx->ch_count; i++) {
+		if (start)
+			mem = g_tas25xx->block_op_data[i].cal_init;
+		else
+			mem = g_tas25xx->block_op_data[i].cal_deinit;
+
+		if (mem) {
+			ret = tas25xx_process_block(g_tas25xx, mem, i);
+			if (ret)
+				dev_err(plat_data->dev, "ch=%d failed to %s device for calibration",
+					i, start ? "init" : "deinit");
+		}
+	}
+}
+EXPORT_SYMBOL(tas25xx_prep_dev_for_calib);
+
+
+static int32_t tas25xx_parse_block_data(struct tas25xx_priv *p_tas25xx, uint8_t **inbuf, int32_t ch)
+{
+	int32_t i = 0;
+	int32_t size;
+	int32_t ret = 0;
+	uint32_t number_of_blocks = 0;
+	enum block_types_t type;
+	uint8_t *out = NULL;
+	uint8_t *start;
+	uint8_t *end;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	start = *inbuf;
+	end = *inbuf;
+
+	end = find_block_for_channel(p_tas25xx, end, BLK_OP_STR, ch);
+	if (!end)
+		return -EINVAL;
+
+	end += HDR_STR_SZ;
+	size = get_block_size_noadvance(end);
+	end += 4;
+
+	number_of_blocks = *((uint32_t *)end);
+	end += sizeof(uint32_t);
+
+	dev_info(plat_data->dev, "parsing block op data, total size=%d, toal blocks=%u",
+		size, number_of_blocks);
+
+	for (i = 0; i < number_of_blocks; i++) {
+		if (memcmp(end, "SWRST", MAIN_BLOCK_SIZE) == 0) {
+			type = BLK_SW_RST;
+		} else if (memcmp(end, "PWRCK", MAIN_BLOCK_SIZE) == 0) {
+			type = BLK_POWER_CHECK;
+		} else if (memcmp(end, "MUTE0", MAIN_BLOCK_SIZE) == 0) {
+			type = BLK_MUTE;
+		} else if (memcmp(end, "RXFMT", MAIN_BLOCK_SIZE) == 0) {
+			type = BLK_RX_FMT;
+		} else if (memcmp(end, "TXFMT", MAIN_BLOCK_SIZE) == 0) {
+			type = BLK_TX_FMT;
+		} else if (memcmp(end, "CLINI", MAIN_BLOCK_SIZE) == 0) {
+			type = BLK_CAL_INIT;
+		} else if (memcmp(end, "CLDIN", MAIN_BLOCK_SIZE) == 0) {
+			type = BLK_CAL_DEINIT;
+		} else {
+			type = BLK_INVALID;
+			dev_err(plat_data->dev, "%s, number of block=%u\n",
+				__func__, number_of_blocks);
+		}
+		end += 5;
+
+		switch (type) {
+		case BLK_SW_RST:
+			end = tas25xx_read_size_bytes(end, &out);
+			if (out)
+				p_tas25xx->block_op_data[ch].sw_reset = out;
+			else
+				ret = -EINVAL;
+			break;
+
+		case BLK_POWER_CHECK:
+			end = tas25xx_read_size_bytes(end, &out);
+			if (out)
+				p_tas25xx->block_op_data[ch].power_check = out;
+			else
+				ret = -EINVAL;
+			break;
+
+		case BLK_MUTE:
+			end = tas25xx_read_size_bytes(end, &out);
+			if (out)
+				p_tas25xx->block_op_data[ch].mute = out;
+			else
+				ret = -EINVAL;
+			break;
+
+		case BLK_CAL_INIT:
+			end = tas25xx_read_size_bytes(end, &out);
+			if (out)
+				p_tas25xx->block_op_data[ch].cal_init = out;
+			else
+				ret = -EINVAL;
+			break;
+
+		case BLK_CAL_DEINIT:
+			end = tas25xx_read_size_bytes(end, &out);
+			if (out)
+				p_tas25xx->block_op_data[ch].cal_deinit = out;
+			else
+				ret = -EINVAL;
+			break;
+
+		case BLK_RX_FMT:
+			end = tas25xx_read_size_bytes(end, &out);
+			dev_info(plat_data->dev, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
+				out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7], out[8], out[9], out[10], out[11]);
+			if (out)
+				p_tas25xx->block_op_data[ch].rx_fmt_data = out;
+			else
+				ret = -EINVAL;
+			break;
+
+		case BLK_TX_FMT:
+			end = tas25xx_read_size_bytes(end, &out);
+			dev_info(plat_data->dev, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
+				out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7], out[8], out[9], out[10], out[11]);
+			if (out)
+				p_tas25xx->block_op_data[ch].tx_fmt_data = out;
+			else
+				ret = -EINVAL;
+			break;
+
+		case BLK_INVALID:
+		default:
+			break;
+
+		}
+	}
+
+	dev_info(plat_data->dev, "%s reset=%p, power-check=%p mute=%p\n", __func__,
+		p_tas25xx->block_op_data[ch].sw_reset, p_tas25xx->block_op_data[ch].power_check,
+		p_tas25xx->block_op_data[ch].mute);
+
+	*inbuf = end;
+
+	return ret;
+}
+
+static int32_t tas25xx_parse_interrupts(struct tas25xx_priv *p_tas25xx, uint8_t **inbuf, int32_t ch)
+{
+	int32_t ret = 0;
+	int32_t size = 0;
+	int32_t i = 0;
+	uint8_t *out = NULL;
+	uint8_t *start;
+	uint8_t *end;
+	int32_t dummy;
+	struct tas25xx_intr_info *intr_info = NULL;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	start = *inbuf;
+	end = *inbuf;
+
+	end = find_block_for_channel(p_tas25xx, end, INTRP_STR, ch);
+	if (!end)
+		return -EINVAL;
+
+	end += HDR_STR_SZ;
+	size = get_block_size_noadvance(end);
+	end += 4;
+	dev_info(plat_data->dev, "parsing interrupt data, total size=%d, ptr=%p",
+		size, end - 4 - 5);
+
+
+	p_tas25xx->intr_data[ch].count = *end;
+	end++;
+
+	dev_info(plat_data->dev, "INTR parsing interrupt buffer, interrupt count=%d",
+		p_tas25xx->intr_data[ch].count);
+
+	end = tas25xx_read_size_bytes(end, &out);
+	if (out)
+		p_tas25xx->intr_data[ch].buf_intr_enable = out;
+	else
+		return -EINVAL;
+
+	end = tas25xx_read_size_bytes(end, &out);
+	if (out)
+		p_tas25xx->intr_data[ch].buf_intr_disable = out;
+	else
+		return -EINVAL;
+
+	end = tas25xx_read_size_bytes(end, &out);
+	if (out)
+		p_tas25xx->intr_data[ch].buf_intr_clear = out;
+	else
+		return -EINVAL;
+
+	dev_info(plat_data->dev, "INTR en=%p dis=%p clr=%p\n",
+		p_tas25xx->intr_data[ch].buf_intr_enable,
+		p_tas25xx->intr_data[ch].buf_intr_disable,
+		p_tas25xx->intr_data[ch].buf_intr_clear);
+
+	dev_info(plat_data->dev, "INTR %c %c %c %c", end[0], end[1], end[2], end[3]);
+
+	size = (p_tas25xx->intr_data[ch].count) * sizeof(struct tas25xx_intr_info);
+	dev_info(plat_data->dev, "%s:%u: ALLOC Size %d\n",
+		__func__, __LINE__, size);
+
+	intr_info = kzalloc(size, GFP_KERNEL);
+	if (!intr_info)
+		return -ENOMEM;
+
+	for (i = 0; i < p_tas25xx->intr_data[ch].count; i++) {
+		memcpy(intr_info[i].name, end, 64);
+		intr_info[i].name[63] = 0;
+		end += 64;
+		memcpy(&(intr_info[i].reg), end, 4);
+		end += 4;
+		memcpy(&(intr_info[i].mask), end, 4);
+		end += 4;
+		memcpy(&(intr_info[i].action), end, 4);
+		end += 4;
+		memcpy(&(intr_info[i].is_clock_based), end, 4);
+		end += 4;
+		memcpy(&(intr_info[i].notify_int_val), end, 4);
+		end += 4;
+		/* 2 dummy ints for future reference */
+		memcpy(&dummy, end, 4);
+		end += 4;
+		memcpy(&dummy, end, 4);
+		end += 4;
+
+		dev_info(plat_data->dev,
+			"INTR %s, REG=%x, MASK=%x, action=%d, clk based=%d notify=%d", intr_info[i].name,
+			intr_info[i].reg, intr_info[i].mask, intr_info[i].action,
+			intr_info[i].is_clock_based, intr_info[i].notify_int_val);
+	}
+	p_tas25xx->intr_data[ch].intr_info = intr_info;
+
+	p_tas25xx->intr_data[ch].processing_delay = *((uint32_t *)end);
+	end += 4;
+
+	/* 5 dummy ints for future reference */
+	dummy = *((uint32_t *)end);
+	end += 4;
+	dummy = *((uint32_t *)end);
+	end += 4;
+	dummy = *((uint32_t *)end);
+	end += 4;
+	dummy = *((uint32_t *)end);
+	end += 4;
+	dummy = *((uint32_t *)end);
+	end += 4;
+
+	if (!ch)
+		dev_info(plat_data->dev, "INTR processing_delay=%d",
+			p_tas25xx->intr_data[ch].processing_delay);
+
+	size = end - start;
+	if (size < 0)
+		size = -1 * size;
+
+	dev_info(plat_data->dev, "INTR bin buf size=%d (end=%p - start=%p)",
+		size, end, start);
+	*inbuf = end;
+
+	return ret;
+}
+
+static int32_t tas25xx_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int32_t ret = -EINVAL;
+	int32_t curr_kcontrol = 0;
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+
+	if (!mc) {
+		pr_err("%s:codec or control is NULL\n", __func__);
+		return ret;
+	}
+
+	curr_kcontrol = mc->reg;
+	if (curr_kcontrol < g_no_of_kcontrols)
+		ucontrol->value.integer.value[0] =
+			g_kctrl_data[curr_kcontrol].kcontrol.int_type.curr_val;
+	else
+		return ret;
+	return 0;
+}
+
+static int32_t tas25xx_int_put_idx_value(struct tas25xx_priv *p_tas25xx,
+	uint32_t ctrl_idx, int32_t value_idx)
+{
+	int ret = 0;
+	int32_t reg_w;
+	int32_t count_w;
+	int32_t chn;
+	int32_t mask_w;
+	int32_t value_w;
+	struct linux_platform *plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	if (ctrl_idx < g_no_of_kcontrols) {
+		reg_w = g_kctrl_data[ctrl_idx].kcontrol.int_type.reg;
+		count_w = g_kctrl_data[ctrl_idx].kcontrol.int_type.count;
+		chn = g_kctrl_data[ctrl_idx].kcontrol.int_type.channel;
+		mask_w = g_kctrl_data[ctrl_idx].kcontrol.int_type.mask;
+		value_w = 0;
+
+		dev_info(plat_data->dev, "%s kcontrol=%s with value index=%d", __func__,
+			g_kctrl_data[ctrl_idx].kcontrol.int_type.name, value_idx);
+
+		if ((value_idx >= 0) && (value_idx < count_w)) {
+			switch (g_kctrl_data[ctrl_idx].kcontrol.int_type.reg_type) {
+			case CMD_SINGLE_WRITE:
+				value_w = g_kctrl_data[ctrl_idx].kcontrol.int_type.chardata[value_idx];
+				ret = p_tas25xx->write(p_tas25xx, chn, reg_w,
+						value_w);
+				if (ret) {
+					dev_err(plat_data->dev, "tas25xx:%s failed ret = %d\n", __func__, ret);
+				} else {
+					dev_info(plat_data->dev,
+						"ch=%d Cmd = %s B:P:R %02x:%02x:%02x, value=%02x, ret=%d\n", chn,
+						CMD_ID[CMD_SINGLE_WRITE], TAS25XX_BOOK_ID(reg_w), TAS25XX_PAGE_ID(reg_w),
+						TAS25XX_PAGE_REG(reg_w), value_w, ret);
+				}
+				break;
+
+			case CMD_BURST_WRITES:
+				value_w = g_kctrl_data[ctrl_idx].kcontrol.int_type.intdata[value_idx];
+				change_endian(&value_w, sizeof(int32_t));
+				ret = p_tas25xx->bulk_write(p_tas25xx, chn,
+						reg_w,
+						(char *)(&value_w), sizeof(int32_t));
+				if (ret) {
+					dev_err(plat_data->dev, "tas25xx:%s failed ret = %d\n", __func__, ret);
+				} else {
+					dev_info(plat_data->dev, "ch=%d Cmd = %s B:P:R %02x:%02x:%02x, value=%02x, ret=%d\n",
+						chn, CMD_ID[CMD_BURST_WRITES], TAS25XX_BOOK_ID(reg_w), TAS25XX_PAGE_ID(reg_w),
+						TAS25XX_PAGE_REG(reg_w), value_w, ret);
+				}
+				break;
+
+			case CMD_UPDATE_BITS:
+				value_w = g_kctrl_data[ctrl_idx].kcontrol.int_type.chardata[value_idx];
+				ret = p_tas25xx->update_bits(p_tas25xx,
+						chn, reg_w, mask_w, value_w);
+				if (ret) {
+					dev_err(plat_data->dev, "tas25xx:%s failed ret = %d\n", __func__, ret);
+				} else {
+					dev_info(plat_data->dev,
+						"ch=%d Cmd = %s B:P:R %02x:%02x:%02x, mask=%02x, value=%02x, ret=%d\n",
+						chn, CMD_ID[CMD_UPDATE_BITS], TAS25XX_BOOK_ID(reg_w), TAS25XX_PAGE_ID(reg_w),
+						TAS25XX_PAGE_REG(reg_w), mask_w, value_w, ret);
+				}
+				break;
+			}
+		} else {
+			dev_err(plat_data->dev, "tas25xx:%s out of bound, value_idx=%d\n", __func__, value_idx);
+			ret = -EINVAL;
+		}
+	} else {
+		dev_err(plat_data->dev, "tas25xx:%s out of bound ctrl_idx=%d\n", __func__, ctrl_idx);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int32_t tas25xx_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct tas25xx_priv *p_tas25xx = NULL;
+	struct linux_platform *plat_data = NULL;
+	int32_t ret = -EINVAL;
+	uint32_t curr_kcontrol = 0;
+	uint32_t misc_info;
+
+	if ((codec == NULL) || (mc == NULL)) {
+		pr_err("tas25xx: %s:codec or control is NULL\n", __func__);
+		return ret;
+	}
+
+	p_tas25xx = snd_soc_component_get_drvdata(codec);
+	if (p_tas25xx == NULL) {
+		pr_err("tas25xx: %s:p_tas25xx is NULL\n", __func__);
+		return ret;
+	}
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	mutex_lock(&p_tas25xx->codec_lock);
+
+	curr_kcontrol = mc->reg;
+	if (curr_kcontrol < g_no_of_kcontrols) {
+		int32_t value_idx = ucontrol->value.integer.value[0];
+		misc_info = g_kctrl_data[curr_kcontrol].kcontrol.int_type.misc_info;
+
+		if (((misc_info & 0xf) == KCNTR_ANYTIME) ||
+						(p_tas25xx->m_power_state == TAS_POWER_ACTIVE))
+			ret = tas25xx_int_put_idx_value(p_tas25xx, curr_kcontrol, value_idx);
+		else
+			ret = 0;
+
+		if (!ret)
+			g_kctrl_data[curr_kcontrol].kcontrol.int_type.curr_val = value_idx;
+	} else {
+		dev_err(plat_data->dev, "tas25xx:%s invalid kcontrol %d\n", __func__, curr_kcontrol);
+	}
+	mutex_unlock(&p_tas25xx->codec_lock);
+
+	return ret;
+}
+
+static int32_t tas25xx_enum_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int32_t ret = -EINVAL;
+	int32_t i = 0;
+
+	if (ucontrol == NULL) {
+		pr_err("%s:ucontrol is NULL\n", __func__);
+		return ret;
+	}
+
+	while (i < g_no_of_kcontrols) {
+		if (g_kctrl_data[i].type == 1) {
+			if (strnstr(ucontrol->id.name,
+						g_kctrl_data[i].kcontrol.enum_type.name, 64)) {
+				ucontrol->value.integer.value[0] =
+					g_kctrl_data[i].kcontrol.enum_type.curr_val;
+				ret = 0;
+			}
+		}
+		i++;
+	}
+	return ret;
+}
+
+static int32_t tas25xx_enum_put_idx_value(struct tas25xx_priv *p_tas25xx,
+	int32_t ctrl_idx, int32_t value_idx)
+{
+	int ret = 0;
+	int32_t count_w = g_kctrl_data[ctrl_idx].kcontrol.enum_type.count;
+	int32_t channel = g_kctrl_data[ctrl_idx].kcontrol.enum_type.channel;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	dev_info(plat_data->dev, "%s kcontrol=%s with value index=%d", __func__,
+		g_kctrl_data[ctrl_idx].kcontrol.enum_type.name, value_idx);
+
+	if ((value_idx >= 0) && (value_idx < count_w)) {
+		char *mem = g_kctrl_data[ctrl_idx].kcontrol.enum_type.data[value_idx].data;
+		ret = tas25xx_process_block(p_tas25xx, mem, channel);
+		if (ret)
+			dev_err(plat_data->dev,
+				"tas25xx:%s failed ret = %d\n", __func__, ret);
+	} else {
+		dev_err(plat_data->dev,
+			"tas25xx:%s out of bound, value_idx=%d\n", __func__, value_idx);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int32_t tas25xx_enum_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct tas25xx_priv *p_tas25xx = NULL;
+	struct linux_platform *plat_data = NULL;
+	int32_t ret = -EINVAL;
+	int32_t i = 0;
+	uint32_t misc_info;
+
+	if ((codec == NULL) || (mc == NULL)) {
+		pr_err("%s:codec or control is NULL\n", __func__);
+		return ret;
+	}
+
+	p_tas25xx = snd_soc_component_get_drvdata(codec);
+	if (p_tas25xx == NULL) {
+		pr_err("%s:p_tas25xx is NULL\n", __func__);
+		return ret;
+	}
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	mutex_lock(&p_tas25xx->codec_lock);
+
+	while (i < g_no_of_kcontrols) {
+		if (g_kctrl_data[i].type == 1) {
+			if (strnstr(ucontrol->id.name,
+						g_kctrl_data[i].kcontrol.enum_type.name, 64)) {
+				int32_t v_idx = ucontrol->value.integer.value[0];
+				misc_info = g_kctrl_data[i].kcontrol.enum_type.misc_info;
+
+				/* check if it needs to be updated now */
+				if (((misc_info & 0xf) == KCNTR_ANYTIME) ||
+						(p_tas25xx->m_power_state == TAS_POWER_ACTIVE))
+					ret = tas25xx_enum_put_idx_value(p_tas25xx, i, v_idx);
+				else
+					ret = 0; /* mixer control will be updated during power up */
+
+				if (!ret)
+					g_kctrl_data[i].kcontrol.enum_type.curr_val = v_idx;
+			}
+		}
+		i++;
+	}
+
+	mutex_unlock(&p_tas25xx->codec_lock);
+
+	return ret;
+}
+
+/*
+ * Helper function to update the kcontrol local values to device
+ * when something like SW/HW reset happens.
+ */
+int32_t tas25xx_update_kcontrol_data(struct tas25xx_priv *p_tas25xx,
+	enum kcntl_during_t cur_state, uint32_t chmask)
+{
+	int ret = -EINVAL;
+	int i = 0, kcntrl_type, misc_info;
+	int chn;
+	char *name = NULL;
+	struct linux_platform *plat_data = NULL;
+	struct tas25xx_kcontrol_int *int_type;
+	struct tas25xx_kcontrol_enum *enum_type;
+
+	if (!p_tas25xx || !g_kctrl_data) {
+		pr_err("%s: p_tas25xx=%p g_kctrl_data=%p\n",
+			__func__, p_tas25xx, g_kctrl_data);
+		ret = -EINVAL;
+		return ret;
+	}
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+	if (!plat_data)
+		return ret;
+
+	for (i = 0; i < g_no_of_kcontrols; ++i) {
+		kcntrl_type = g_kctrl_data[i].type;
+		ret = 0;
+
+		switch (kcntrl_type) {
+		/* integer */
+		case 0:
+			int_type = &g_kctrl_data[i].kcontrol.int_type;
+			chn = g_kctrl_data[i].kcontrol.int_type.channel;
+			if ((1 << chn) & chmask) {
+			misc_info = int_type->misc_info;
+			name = int_type->name;
+			if (cur_state == (misc_info & 0xf))
+				ret = tas25xx_int_put_idx_value(p_tas25xx, i,
+					int_type->curr_val);
+			}
+			break;
+
+		/* enum */
+		case 1:
+			enum_type = &g_kctrl_data[i].kcontrol.enum_type;
+			chn = g_kctrl_data[i].kcontrol.enum_type.channel;
+			if ((1 << chn) & chmask) {
+			misc_info = enum_type->misc_info;
+			name = enum_type->name;
+			if (cur_state == (misc_info & 0xf))
+				ret = tas25xx_enum_put_idx_value(p_tas25xx, i,
+					enum_type->curr_val);
+			}
+			break;
+
+		default:
+			name = NULL;
+			dev_err(plat_data->dev,
+				"%s non supported kcontrol type\n", __func__);
+			ret = -EINVAL;
+			break;
+
+		}
+
+		if (ret) {
+			dev_err(plat_data->dev, "%s %s mixer ctrl update err=%d\n",
+				__func__, name ? name : "", ret);
+			continue;
+		}
+	}
+
+	return ret;
+}
+
+
+static int32_t tas25xx_create_common_kcontrols(struct tas25xx_priv *p_tas25xx)
+{
+	struct linux_platform *plat_data =
+		(struct linux_platform *)p_tas25xx->platform_data;
+	int32_t ret = 0;
+	int32_t i = 0;
+
+	if (!g_no_of_kcontrols) {
+		dev_info(plat_data->dev, "%s no kcontrols found", __func__);
+		return 0;
+	}
+
+	g_kctrl_ctrl = kzalloc(g_no_of_kcontrols * sizeof(struct snd_kcontrol_new), GFP_KERNEL);
+	if (!g_kctrl_ctrl) {
+		ret = -ENOMEM;
+		goto EXIT;
+	}
+	for (i = 0; i < g_no_of_kcontrols; i++) {
+		/* Integer Type */
+		if (g_kctrl_data[i].type == 0) {
+			g_kctrl_data[i].kcontrol.int_type.mctl.reg = i;
+			g_kctrl_data[i].kcontrol.int_type.mctl.rreg = i;
+			g_kctrl_data[i].kcontrol.int_type.mctl.shift = 0;
+			g_kctrl_data[i].kcontrol.int_type.mctl.rshift = 0;
+			g_kctrl_data[i].kcontrol.int_type.mctl.max = g_kctrl_data[i].kcontrol.int_type.count-1;
+			g_kctrl_data[i].kcontrol.int_type.mctl.platform_max = g_kctrl_data[i].kcontrol.int_type.count-1;
+			g_kctrl_data[i].kcontrol.int_type.mctl.invert = 0;
+			g_kctrl_data[i].kcontrol.int_type.mctl.autodisable = 0;
+
+			g_kctrl_ctrl[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+			g_kctrl_ctrl[i].name = g_kctrl_data[i].kcontrol.int_type.name;
+			g_kctrl_ctrl[i].info = snd_soc_info_volsw;
+			g_kctrl_ctrl[i].get = tas25xx_get;
+			g_kctrl_ctrl[i].put = tas25xx_put;
+			g_kctrl_ctrl[i].private_value =
+				(unsigned long)(&(g_kctrl_data[i].kcontrol.int_type.mctl));
+		} else { /* Enum Type */
+			int32_t count = g_kctrl_data[i].kcontrol.enum_type.count;
+
+			g_kctrl_data[i].kcontrol.enum_type.tas25xx_kcontrol_enum.items = count;
+			g_kctrl_data[i].kcontrol.enum_type.tas25xx_kcontrol_enum.texts =
+				(const char * const *)g_kctrl_data[i].kcontrol.enum_type.enum_texts;
+
+			g_kctrl_ctrl[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+			g_kctrl_ctrl[i].name = g_kctrl_data[i].kcontrol.enum_type.name;
+			g_kctrl_ctrl[i].info = snd_soc_info_enum_double;
+			g_kctrl_ctrl[i].get = tas25xx_enum_get;
+			g_kctrl_ctrl[i].put = tas25xx_enum_put;
+			g_kctrl_ctrl[i].private_value =
+				(unsigned long)(&(g_kctrl_data[i].kcontrol.enum_type.tas25xx_kcontrol_enum));
+		}
+	}
+	ret = snd_soc_add_component_controls(plat_data->codec,
+			g_kctrl_ctrl, g_no_of_kcontrols);
+EXIT:
+	return ret;
+}
+
+static int32_t tas25xx_parse_kcontrols(struct tas25xx_priv *p_tas25xx, uint8_t **inbuf)
+{
+	int32_t ret = 0;
+	int32_t size = 0;
+	int32_t j = 0, i = 0;
+	int32_t count;
+	struct linux_platform *plat_data = NULL;
+	uint8_t *buf = *inbuf;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	buf = find_block_for_channel(p_tas25xx, buf, KCNTRL_STR, ANY_CHANNEL);
+	if (!buf)
+		return -EINVAL;
+
+	buf += HDR_STR_SZ;
+	size = get_block_size_noadvance(buf);
+	buf += 4;
+	dev_info(plat_data->dev, "parsing kcontrol data, total size=%d", size);
+
+	memcpy(&g_no_of_kcontrols, buf, sizeof(g_no_of_kcontrols));
+	buf += sizeof(g_no_of_kcontrols);
+
+	dev_err(plat_data->dev, "%s: g_no_of_kcontrols %d\n", __func__, g_no_of_kcontrols);
+
+	g_kctrl_data = kzalloc(g_no_of_kcontrols * sizeof(struct tas25xx_kcontrols), GFP_KERNEL);
+	if (!g_kctrl_data) {
+		ret = -ENOMEM;
+		g_no_of_kcontrols = 0;
+		goto EXIT;
+	}
+
+	for (i = 0; i < g_no_of_kcontrols; i++) {
+		memcpy(&(g_kctrl_data[i].type), buf, 1);
+		buf += 1;
+		/* Integer Type */
+		if (g_kctrl_data[i].type == 0) {
+
+			g_kctrl_data[i].kcontrol.int_type.name = kasprintf(GFP_KERNEL, "%s %s",
+					"TAS25XX", (char *)buf);
+			buf += 64;
+			memcpy(&(g_kctrl_data[i].kcontrol.int_type.channel), buf, 1);
+			buf += 1;
+			memcpy(&(g_kctrl_data[i].kcontrol.int_type.reg), buf, 4);
+			buf += 4;
+			memcpy(&(g_kctrl_data[i].kcontrol.int_type.reg_type), buf, 1);
+			buf += 1;
+			if (g_kctrl_data[i].kcontrol.int_type.reg_type == 2) {
+				memcpy(&(g_kctrl_data[i].kcontrol.int_type.mask), buf, 4);
+				buf += 4;
+			}
+			memcpy(&(g_kctrl_data[i].kcontrol.int_type.range_min), buf, 1);
+			buf += 4;
+			memcpy(&(g_kctrl_data[i].kcontrol.int_type.range_max), buf, 4);
+			buf += 4;
+			memcpy(&(g_kctrl_data[i].kcontrol.int_type.step), buf, 4);
+			buf += 4;
+			memcpy(&(g_kctrl_data[i].kcontrol.int_type.def), buf, 4);
+			buf += 4;
+			if (s_rbin.head.version > 6) {
+				memcpy(&(g_kctrl_data[i].kcontrol.int_type.misc_info), buf, 4);
+				buf += 4;
+			}
+
+			/* Assign default value to current value */
+			g_kctrl_data[i].kcontrol.int_type.curr_val = g_kctrl_data[i].kcontrol.int_type.def;
+			dev_err(plat_data->dev, "%s: curr_kcontrol %d\n", __func__, g_kctrl_data[i].kcontrol.int_type.curr_val);
+			memcpy(&(g_kctrl_data[i].kcontrol.int_type.count), buf, 4);
+			buf += 4;
+			if (g_kctrl_data[i].kcontrol.int_type.reg_type == 1) {
+				g_kctrl_data[i].kcontrol.int_type.intdata =
+						kzalloc(g_kctrl_data[i].kcontrol.int_type.count * sizeof(int32_t), GFP_KERNEL);
+				for (j = 0; j < g_kctrl_data[i].kcontrol.int_type.count; j++) {
+					memcpy(&(g_kctrl_data[i].kcontrol.int_type.intdata[j]), buf, 4);
+					buf += 4;
+				}
+			} else {
+				g_kctrl_data[i].kcontrol.int_type.chardata = kzalloc(g_kctrl_data[i].kcontrol.int_type.count * sizeof(char), GFP_KERNEL);
+				for (j = 0; j < g_kctrl_data[i].kcontrol.int_type.count; j++) {
+					memcpy(&(g_kctrl_data[i].kcontrol.int_type.chardata[j]), buf, 1);
+					buf += 1;
+				}
+			}
+		} else { /* Enum type */
+			g_kctrl_data[i].kcontrol.enum_type.name = kasprintf(GFP_KERNEL, "%s %s",
+					"TAS25XX", (char *)buf);
+			buf += 64;
+			memcpy(&(g_kctrl_data[i].kcontrol.enum_type.channel), buf, 1);
+			buf += 1;
+			memcpy(&(g_kctrl_data[i].kcontrol.enum_type.def), buf, 1);
+			buf += 1;
+			if (s_rbin.head.version > 6) {
+				memcpy(&(g_kctrl_data[i].kcontrol.enum_type.misc_info), buf, 4);
+				buf += 4;
+			}
+			g_kctrl_data[i].kcontrol.enum_type.curr_val =
+				g_kctrl_data[i].kcontrol.enum_type.def;
+			memcpy(&count, buf, 4);
+			buf += 4;
+			g_kctrl_data[i].kcontrol.enum_type.count = count;
+
+			g_kctrl_data[i].kcontrol.enum_type.data =
+				kzalloc(count * sizeof(struct tas25xx_kcontrol_enum_data), GFP_KERNEL);
+			if (!g_kctrl_data[i].kcontrol.enum_type.data) {
+				ret = -ENOMEM;
+				goto EXIT;
+			}
+			for (j = 0; j < count; j++) {
+				memcpy(g_kctrl_data[i].kcontrol.enum_type.data[j].name, buf, 64);
+				buf += 64;
+				memcpy(&size, buf, 4);
+				g_kctrl_data[i].kcontrol.enum_type.data[j].data = buf;
+				buf += (size + 4);
+			}
+
+			g_kctrl_data[i].kcontrol.enum_type.enum_texts = kzalloc(count * sizeof(char *), GFP_KERNEL);
+			if (!g_kctrl_data[i].kcontrol.enum_type.enum_texts) {
+				ret = -ENOMEM;
+				goto EXIT;
+			}
+			for (j = 0; j < count; j++) {
+				g_kctrl_data[i].kcontrol.enum_type.enum_texts[j] = kzalloc(64, GFP_KERNEL);
+				if (!g_kctrl_data[i].kcontrol.enum_type.enum_texts[j]) {
+					ret = -ENOMEM;
+					goto EXIT;
+				}
+				memcpy(g_kctrl_data[i].kcontrol.enum_type.enum_texts[j],
+						g_kctrl_data[i].kcontrol.enum_type.data[j].name, 64);
+			}
+		}
+	}
+EXIT:
+	*inbuf = buf;
+	return ret;
+}
+
+int tas_write_init_default_config_params(struct tas25xx_priv *p_tas25xx, int number_of_channels)
+{
+	int i;
+	int ret = 0;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	for (i = 0; i < number_of_channels && !ret; i++) {
+		ret = tas25xx_set_sample_rate(p_tas25xx, i, TAS25XX_DEFAULT);
+		ret = tas25xx_set_fmt_inv(p_tas25xx, i, TAS25XX_DEFAULT);
+		ret = tas25xx_set_fmt_mask(p_tas25xx, i, TAS25XX_DEFAULT);
+		ret = tas25xx_set_rx_slots(p_tas25xx, i, TAS25XX_DEFAULT);
+		ret = tas25xx_set_tx_slots(p_tas25xx, i, TAS25XX_DEFAULT);
+		ret = tas25xx_set_rx_bitwidth(p_tas25xx, i, TAS25XX_DEFAULT);
+		ret = tas25xx_set_rx_slotlen(p_tas25xx, i, TAS25XX_DEFAULT);
+		ret = tas25xx_set_tx_slotlen(p_tas25xx, i, TAS25XX_DEFAULT);
+	}
+
+	return ret;
+}
+
+int tas_write_init_config_params(struct tas25xx_priv *p_tas25xx, int number_of_channels)
+{
+	return tas_write_init_default_config_params(p_tas25xx, number_of_channels);
+}
+
+static int32_t tas25xx_parse_algo_data(struct tas25xx_priv *p_tas25xx, uint8_t **inbuf)
+{
+	int32_t ret = 0;
+	struct linux_platform *plat_data = NULL;
+	uint8_t *buf = *inbuf;
+	uint32_t size;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	buf = find_block_for_channel(p_tas25xx, buf, ALGOP_STR, ANY_CHANNEL);
+	if (!buf)
+		return -EINVAL;
+
+	buf += HDR_STR_SZ;
+	size = get_block_size_noadvance(buf);
+	buf += 4;
+	dev_info(plat_data->dev, "parsing algo data, total size=%d", size);
+
+#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
+	tas25xx_parse_algo_bin(p_tas25xx->ch_count, buf);
+#endif /* CONFIG_TAS25XX_ALGO */
+
+	return ret;
+}
+
+static void tas25xx_fw_ready(const struct firmware *pFW, void *pContext)
+{
+	int32_t ret;
+	uint32_t size;
+	int32_t i;
+	int32_t number_of_channels;
+	uint32_t rev_count;
+	uint32_t rev_id;
+	uint64_t file_offset;
+	struct tas25xx_priv *p_tas25xx = (struct tas25xx_priv *) pContext;
+	struct linux_platform *plat_data = NULL;
+	struct i2c_client *p_client = NULL;
+	uint8_t *bin_data = NULL;
+	uint8_t *mdata;
+	struct bin_header *hdrp;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+	p_client = container_of(plat_data->dev,
+		struct i2c_client, dev);
+
+	if (unlikely(!pFW) || unlikely(!pFW->data)) {
+		dev_err(plat_data->dev,
+			"firmware is not ready, %s\n", TAS25XX_BINFILE_NAME);
+		atomic_set(&p_tas25xx->fw_state, TAS25XX_DSP_FW_TRYLOAD);
+		goto EXIT;
+	}
+
+	dev_info(plat_data->dev, "%s: pFW->size %d", __func__, (int32_t)pFW->size);
+	atomic_set(&p_tas25xx->fw_state, TAS25XX_DSP_FW_PARSE_FAIL);
+
+	mdata = (uint8_t *)pFW->data;
+	if (header_check(p_tas25xx, MDATA, mdata)) {
+		dev_info(plat_data->dev, "%s: Found metadata, check against rev-id %d",
+			__func__, p_tas25xx->dev_revid);
+		file_offset = 0;
+		size = 0;
+		mdata += 5; /* header */
+		memcpy(&rev_count, mdata, sizeof(uint32_t));
+		mdata += 4; /* count */
+		for (i = 0; i < rev_count; i++) {
+			file_offset += size;
+
+			memcpy(&rev_id, mdata, sizeof(uint32_t));
+			mdata += 4; /* id */
+			memcpy(&size, mdata, sizeof(uint32_t));
+			mdata += 4; /* file sz */
+			/* dummy data */
+			mdata += 4;
+			mdata += 4;
+			mdata += 4;
+			dev_info(plat_data->dev, "%s: revid 0x%x, size=%d, offset=%llu",
+				__func__, rev_id, size, file_offset);
+			if (p_tas25xx->dev_revid == rev_id)
+				break;
+		}
+
+		if (i == rev_count) {
+			dev_info(plat_data->dev,
+				"%s: metadata parse failure...", __func__);
+			ret = -EINVAL;
+			goto EXIT;
+		} else {
+			dev_info(plat_data->dev,
+				"%s: using idx=%d revid 0x%x, size=%d", __func__, i, rev_id, size);
+			p_tas25xx->fw_data = kzalloc(size, GFP_KERNEL);
+			if (!p_tas25xx->fw_data) {
+				ret = -ENOMEM;
+				goto EXIT;
+			}
+
+			bin_data = (uint8_t *)pFW->data;
+			bin_data += MDATA_HDR_SZ;
+
+			/* first bin file */
+			bin_data += (rev_count * ONE_BIN_MD_SZ) + file_offset;
+
+
+			p_tas25xx->fw_size = size;
+			memcpy(p_tas25xx->fw_data, bin_data, size);
+		}
+
+	} else {
+		dev_info(plat_data->dev, "%s: metadata not found, regular fw", __func__);
+			p_tas25xx->fw_data = kzalloc(pFW->size, GFP_KERNEL);
+		if (!p_tas25xx->fw_data) {
+			ret = -ENOMEM;
+			goto EXIT;
+		}
+		p_tas25xx->fw_size = pFW->size;
+		memcpy(p_tas25xx->fw_data, pFW->data, pFW->size);
+	}
+
+	bin_data = p_tas25xx->fw_data;
+
+	if (!header_check(p_tas25xx, HEADER, bin_data)) {
+		ret = -EINVAL;
+		goto EXIT;
+	} else {
+		bin_data += HDR_STR_SZ;
+		size = get_block_size_noadvance(bin_data);
+		dev_info(plat_data->dev, "%s: Header size %d", __func__, size);
+		bin_data += 4;
+	}
+
+	/* header parsing */
+	hdrp = (struct bin_header *)bin_data;
+	s_rbin.head.version = hdrp->version;
+	memcpy(s_rbin.head.name, hdrp->name, 64);
+	s_rbin.head.timestamp = hdrp->timestamp;
+	s_rbin.head.size = hdrp->size;
+	s_rbin.head.channels = hdrp->channels;
+	bin_data += offsetof(struct bin_header, dev);
+	for (i = 0; i < s_rbin.head.channels; i++) {
+		s_rbin.head.dev[i].device = bin_data[0];
+		s_rbin.head.dev[i].i2c_addr = bin_data[1];
+		bin_data += 2;
+	}
+	s_rbin.head.iv_width = *bin_data;
+	bin_data++;
+	s_rbin.head.vbat_mon = *bin_data;
+	bin_data++;
+	s_rbin.head.features = *((uint32_t *)bin_data);
+	bin_data += sizeof(uint32_t);
+	/* end of header */
+
+	memcpy(&s_rbin.def_hw_params, bin_data, sizeof(struct default_hw_params));
+	bin_data += sizeof(struct default_hw_params);
+
+	/* skip dummy data */
+	bin_data += (sizeof(int32_t) * 5);
+
+	number_of_channels = s_rbin.head.channels;
+
+	dev_info(plat_data->dev, "%s: version 0x%x", __func__, s_rbin.head.version);
+	dev_info(plat_data->dev, "%s: name %s", __func__, s_rbin.head.name);
+	dev_info(plat_data->dev, "%s: timestamp %d", __func__, s_rbin.head.timestamp);
+	dev_info(plat_data->dev, "%s: channels %d", __func__, number_of_channels);
+
+	for (i = 0; i < number_of_channels; i++) {
+		dev_info(plat_data->dev, "%s: device_%d %d", __func__, i,
+				s_rbin.head.dev[i].device);
+		if (p_tas25xx->devs[i]->mn_addr < 0) {
+			dev_info(plat_data->dev, "%s: Updating the device_%d i2c address to 0x%x from invalid",
+				__func__, i, s_rbin.head.dev[i].i2c_addr);
+			p_tas25xx->devs[i]->mn_addr = s_rbin.head.dev[i].i2c_addr;
+		}
+	}
+
+	for (i = 0; i < number_of_channels; i++)
+		if (p_tas25xx->devs[i]->mn_addr == p_client->addr)
+			break;
+	if (i == number_of_channels)
+		dev_warn(plat_data->dev,
+			"Atlest one address should be %d", p_client->addr);
+
+	dev_info(plat_data->dev, "%s: IVSenseWidth %d", __func__, (int)s_rbin.head.iv_width);
+	dev_info(plat_data->dev, "%s: Vbat-mon %d", __func__, (int)s_rbin.head.vbat_mon);
+	p_tas25xx->mn_iv_width = p_tas25xx->curr_mn_iv_width = (int)s_rbin.head.iv_width;
+	p_tas25xx->mn_vbat = p_tas25xx->curr_mn_vbat = (int)s_rbin.head.vbat_mon;
+	dev_dbg(plat_data->dev, "%s: updating number of channels %d -> %d",
+		__func__, p_tas25xx->ch_count, number_of_channels);
+	p_tas25xx->ch_count = number_of_channels;
+
+	dev_dbg(plat_data->dev, "%s: features %d", __func__, s_rbin.head.features);
+	dev_dbg(plat_data->dev, "%s: sample_rate %s", __func__, SampleRate[(int32_t)s_rbin.def_hw_params.sample_rate]);
+	dev_dbg(plat_data->dev, "%s: fmt_inv %s", __func__, FMT_INV[(int32_t)s_rbin.def_hw_params.fmt_inv]);
+	dev_dbg(plat_data->dev, "%s: fmt_mask %s", __func__, FMT_MASK[(int32_t)s_rbin.def_hw_params.fmt_mask]);
+	dev_dbg(plat_data->dev, "%s: rx_slots %s", __func__, RX_SLOTS[(int32_t)s_rbin.def_hw_params.rx_slots]);
+	dev_dbg(plat_data->dev, "%s: tx_slots %s", __func__, TX_SLOTS[(int32_t)s_rbin.def_hw_params.tx_slots]);
+	dev_dbg(plat_data->dev, "%s: rx_bitwidth %s", __func__, RX_SLOTS[(int32_t)s_rbin.def_hw_params.rx_bitwidth]);
+	dev_dbg(plat_data->dev, "%s: rx_slotlen %s", __func__, RX_SLOTS[(int32_t)s_rbin.def_hw_params.rx_slotlen]);
+	dev_dbg(plat_data->dev, "%s: tx_slotlen %s", __func__, TX_SLOTS[(int32_t)s_rbin.def_hw_params.tx_slotlen]);
+
+	if (s_rbin.head.version > SUPPORTED_BIN_VERSION) {
+		dev_err(plat_data->dev,
+			"%s: version not compatible. Supported version <= 0x%x", __func__,
+			SUPPORTED_BIN_VERSION);
+		goto EXIT;
+	}
+
+	for (i = 0; i < number_of_channels; i++) {
+		/* Parsing Init Params */
+		ret = tas25xx_parse_init_params(p_tas25xx, &bin_data, i);
+		if (ret) {
+			dev_err(plat_data->dev, "%s: Init Parsing failed", __func__);
+			goto EXIT;
+		}
+		ret = tas25xx_parse_hw_params(p_tas25xx, &bin_data, i);
+		if (ret) {
+			dev_err(plat_data->dev,
+				"%s: HW params parsing failed, ignoring..", __func__);
+		}
+		ret = tas25xx_parse_profiles(p_tas25xx, &bin_data, i);
+		if (ret) {
+			dev_err(plat_data->dev, "%s: profiles failed", __func__);
+			goto EXIT;
+		}
+		ret = tas25xx_parse_interrupts(p_tas25xx, &bin_data, i);
+		if (ret) {
+			dev_info(plat_data->dev,
+				"%s: interrupt parsing failed, ignoring..", __func__);
+		}
+
+		ret = tas25xx_parse_block_data(p_tas25xx, &bin_data, i);
+		if (ret) {
+			dev_err(plat_data->dev, "%s: block data", __func__);
+			goto EXIT;
+		}
+	}
+
+	ret = tas25xx_parse_kcontrols(p_tas25xx, &bin_data);
+	if (ret)
+		dev_info(plat_data->dev,
+			"%s: Error while parsing the kcontrols, ignored..", __func__);
+
+	ret = tas25xx_parse_algo_data(p_tas25xx, &bin_data);
+	if (ret)
+		dev_info(plat_data->dev,
+			"%s: Error while parsing the algo data, ignored..", __func__);
+
+	dev_dbg(plat_data->dev, "%s: Firmware init complete\n", __func__);
+
+	atomic_set(&p_tas25xx->fw_state, TAS25XX_DSP_FW_OK);
+
+EXIT:
+	atomic_set(&p_tas25xx->fw_wait_complete, 1);
+	wake_up(&p_tas25xx->fw_wait);
+
+	if (pFW)
+		release_firmware(pFW);
+}
+
+
+int32_t tas25xx_load_firmware(struct tas25xx_priv *p_tas25xx, int max_fw_tryload_count)
+{
+	int32_t ret;
+	uint32_t retry_count = 0;
+	struct linux_platform *plat_data = NULL;
+
+	g_tas25xx = p_tas25xx;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	if (!max_fw_tryload_count)
+		max_fw_tryload_count = DSP_FW_LOAD_NTRIES;
+	atomic_set(&p_tas25xx->fw_wait_complete, 0);
+
+	do {
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+				TAS25XX_BINFILE_NAME, plat_data->dev, GFP_KERNEL,
+				p_tas25xx, tas25xx_fw_ready);
+		if (ret) {
+			dev_err(plat_data->dev, "request_firmware_nowait failed, err=%d", ret);
+			msleep(100);
+		} else {
+			dev_info(plat_data->dev, "fw load requested, trail=%d", retry_count);
+			/* wait for either firmware to be loaded or failed */
+			ret = wait_event_interruptible(p_tas25xx->fw_wait,
+				atomic_read(&p_tas25xx->fw_wait_complete));
+			if (ret)
+				dev_err(plat_data->dev, "wait failed with error %d", ret);
+
+			/* any other state other than retry */
+			if (atomic_read(&p_tas25xx->fw_state) != TAS25XX_DSP_FW_TRYLOAD)
+				break;
+
+			atomic_set(&p_tas25xx->fw_wait_complete, 0);
+			msleep(100);
+		}
+
+		retry_count++;
+	} while (retry_count < max_fw_tryload_count);
+
+	if (retry_count == max_fw_tryload_count)
+		atomic_set(&p_tas25xx->fw_state, TAS25XX_DSP_FW_LOAD_FAIL);
+
+	switch (atomic_read(&p_tas25xx->fw_state)) {
+	case TAS25XX_DSP_FW_LOAD_FAIL:
+		ret = -ENOENT;
+		break;
+	case TAS25XX_DSP_FW_OK:
+		ret = 0;
+		break;
+	case TAS25XX_DSP_FW_PARSE_FAIL:
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+int32_t tas25xx_create_kcontrols(struct tas25xx_priv *p_tas25xx)
+{
+	int32_t ret = -EINVAL;
+	int fw_state;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	fw_state = atomic_read(&p_tas25xx->fw_state);
+	if (fw_state == TAS25XX_DSP_FW_OK) {
+		ret = tas25xx_create_common_kcontrols(p_tas25xx);
+		if (ret) {
+			dev_err(plat_data->dev, "%s: kcontrols failed", __func__);
+			goto EXIT;
+		}
+		ret = tas25xx_create_profile_controls(p_tas25xx);
+		if (ret) {
+			dev_err(plat_data->dev, "%s: kcontrols failed", __func__);
+			goto EXIT;
+		}
+	} else {
+		dev_err(plat_data->dev, "%s: firmware not loaded\n", __func__);
+	}
+EXIT:
+	return ret;
+}
+
+int32_t tas25xx_set_init_params(struct tas25xx_priv *p_tas25xx, int32_t ch)
+{
+	int32_t ret = -EINVAL;
+	int fw_state;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	dev_info(plat_data->dev, "%s: ch-%d\n", __func__, ch);
+	fw_state = atomic_read(&p_tas25xx->fw_state);
+	if (fw_state == TAS25XX_DSP_FW_OK)
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.init_params[ch], ch);
+	else
+		dev_err(plat_data->dev, "%s: firmware not loaded\n", __func__);
+
+	return ret;
+}
+
+int32_t tas25xx_set_sample_rate(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t sample_rate)
+{
+	int32_t ret = -EINVAL;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	p_tas25xx->sample_rate = sample_rate;
+
+	switch (sample_rate) {
+	case 44100:
+		dev_info(plat_data->dev, "%s: ch-%d SampleRate 44100\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].SampleRate[SampleRate_44100], ch);
+		break;
+
+	case 96000:
+		dev_info(plat_data->dev, "%s: ch-%d SampleRate 96000\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].SampleRate[SampleRate_96000], ch);
+		break;
+
+	case 48000:
+		dev_info(plat_data->dev, "%s: ch-%d SampleRate 48000\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].SampleRate[SampleRate_48000], ch);
+		break;
+
+	case TAS25XX_DEFAULT:
+	default:
+		p_tas25xx->sample_rate = TAS25XX_DEFAULT;
+		dev_info(plat_data->dev, "%s:[default] ch-%d SampleRate %s [default]\n", __func__, ch,
+				SampleRate[(int32_t)s_rbin.def_hw_params.sample_rate]);
+		ret = tas25xx_process_block(p_tas25xx,
+			s_rbin.hw_params[ch].SampleRate[(int32_t)s_rbin.def_hw_params.sample_rate], ch);
+		break;
+	}
+	return ret;
+}
+
+int32_t tas25xx_set_fmt_inv(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t fmt_inv)
+{
+	int32_t ret = -EINVAL;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	switch (fmt_inv) {
+	case FMT_INV_NB_NF:
+		dev_info(plat_data->dev, "%s: ch-%d FMT_INV_NB_NF\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].FMT_INV[FMT_INV_NB_NF], ch);
+		break;
+	case FMT_INV_IB_NF:
+		dev_info(plat_data->dev, "%s: ch-%d FMT_INV_IB_NF\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].FMT_INV[FMT_INV_IB_NF], ch);
+		break;
+	case FMT_INV_NB_IF:
+		dev_info(plat_data->dev, "%s: ch-%d FMT_INV_NB_IF\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].FMT_INV[FMT_INV_NB_IF], ch);
+		break;
+	case FMT_INV_IB_IF:
+		dev_info(plat_data->dev, "%s: ch-%d FMT_INV_IB_IF\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].FMT_INV[FMT_INV_IB_IF], ch);
+		break;
+	case TAS25XX_DEFAULT:
+		dev_info(plat_data->dev, "%s:[default] ch-%d %s\n", __func__, ch,
+			FMT_INV[(int32_t)s_rbin.def_hw_params.fmt_inv]);
+		ret = tas25xx_process_block(p_tas25xx,
+			s_rbin.hw_params[ch].FMT_INV[(int32_t)s_rbin.def_hw_params.fmt_inv], ch);
+		break;
+	default:
+		dev_info(plat_data->dev, "%s: ch-%d Invalid FMT_INV %d\n", __func__, ch,
+				fmt_inv);
+		break;
+	}
+	return ret;
+}
+
+int32_t tas25xx_set_fmt_mask(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t fmt_mask)
+{
+	int32_t ret = -EINVAL;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	switch (fmt_mask) {
+	case FMT_MASK_I2S:
+		dev_info(plat_data->dev, "%s: ch-%d FMT_MASK_I2S\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx,
+			s_rbin.hw_params[ch].FMT_MASK[FMT_MASK_I2S], ch);
+		break;
+	case FMT_MASK_DSP_A:
+		dev_info(plat_data->dev, "%s: ch-%d FMT_MASK_DSP_A\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx,
+			s_rbin.hw_params[ch].FMT_MASK[FMT_MASK_DSP_A], ch);
+		break;
+	case FMT_MASK_DSP_B:
+		dev_info(plat_data->dev, "%s: ch-%d FMT_MASK_DSP_B\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx,
+			s_rbin.hw_params[ch].FMT_MASK[FMT_MASK_DSP_B], ch);
+		break;
+	case FMT_MASK_LEFT_J:
+		dev_info(plat_data->dev, "%s: ch-%d FMT_MASK_LEFT_J\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx,
+			s_rbin.hw_params[ch].FMT_MASK[FMT_MASK_LEFT_J], ch);
+		break;
+	case TAS25XX_DEFAULT:
+		dev_info(plat_data->dev, "%s:[default] ch-%d %s\n", __func__, ch,
+				FMT_MASK[(int32_t)s_rbin.def_hw_params.fmt_mask]);
+		ret = tas25xx_process_block(p_tas25xx,
+			s_rbin.hw_params[ch].FMT_MASK[(int32_t)s_rbin.def_hw_params.fmt_mask], ch);
+		break;
+	default:
+		dev_info(plat_data->dev, "%s: ch-%d Invalid FMT_MASK %d\n", __func__, ch,
+				fmt_mask);
+		break;
+	}
+	return ret;
+}
+
+int32_t tas25xx_set_rx_slots(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t rx_slot)
+{
+	int32_t ret = -EINVAL;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	switch (rx_slot) {
+	case RX_SLOTS_16:
+		dev_info(plat_data->dev, "%s: ch-%d RX_SLOTS_16\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].RX_SLOTS[RX_SLOTS_16], ch);
+		break;
+	case RX_SLOTS_24:
+		dev_info(plat_data->dev, "%s: ch-%d RX_SLOTS_24\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].RX_SLOTS[RX_SLOTS_24], ch);
+		break;
+	case RX_SLOTS_32:
+		dev_info(plat_data->dev, "%s: ch-%d RX_SLOTS_32\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].RX_SLOTS[RX_SLOTS_32], ch);
+		break;
+	case TAS25XX_DEFAULT:
+		dev_info(plat_data->dev, "%s:[default] ch-%d %s\n", __func__, ch,
+				RX_SLOTS[(int32_t)s_rbin.def_hw_params.rx_slots]);
+		ret = tas25xx_process_block(p_tas25xx,
+			s_rbin.hw_params[ch].RX_SLOTS[(int32_t)s_rbin.def_hw_params.rx_slots], ch);
+		break;
+	default:
+		dev_info(plat_data->dev, "%s: ch-%d Invalid RX_SLOTS %d\n", __func__, ch,
+				rx_slot);
+		break;
+	}
+	return ret;
+}
+
+int32_t tas25xx_set_tx_slots(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t tx_slot)
+{
+	int32_t ret = -EINVAL;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	switch (tx_slot) {
+	case TX_SLOTS_16:
+		dev_info(plat_data->dev, "%s: ch-%d TX_SLOTS_16\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].TX_SLOTS[TX_SLOTS_16], ch);
+		break;
+	case TX_SLOTS_24:
+		dev_info(plat_data->dev, "%s: ch-%d TX_SLOTS_24\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].TX_SLOTS[TX_SLOTS_24], ch);
+		break;
+	case TX_SLOTS_32:
+		dev_info(plat_data->dev, "%s: ch-%d TX_SLOTS_32\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].TX_SLOTS[TX_SLOTS_32], ch);
+		break;
+	case TAS25XX_DEFAULT:
+		dev_info(plat_data->dev, "%s:[default] ch-%d %s\n", __func__, ch,
+				RX_SLOTS[(int32_t)s_rbin.def_hw_params.tx_slots]);
+		ret = tas25xx_process_block(p_tas25xx,
+			s_rbin.hw_params[ch].TX_SLOTS[(int32_t)s_rbin.def_hw_params.tx_slots], ch);
+		break;
+	default:
+		dev_info(plat_data->dev, "%s: ch-%d Invalid TX_SLOTS %d\n", __func__, ch,
+				tx_slot);
+		break;
+	}
+	return ret;
+}
+
+int32_t tas25xx_set_rx_bitwidth(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t rx_bitwidth)
+{
+	int32_t ret = -EINVAL;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	switch (rx_bitwidth) {
+	case RX_BITWIDTH_16:
+		dev_info(plat_data->dev, "%s: ch-%d RX_BITWIDTH_16\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].RX_BITWIDTH[RX_BITWIDTH_16], ch);
+		break;
+	case RX_BITWIDTH_24:
+		dev_info(plat_data->dev, "%s: ch-%d RX_BITWIDTH_24\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].RX_BITWIDTH[RX_BITWIDTH_24], ch);
+		break;
+	case RX_BITWIDTH_32:
+		dev_info(plat_data->dev, "%s: ch-%d RX_BITWIDTH_32\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].RX_BITWIDTH[RX_BITWIDTH_32], ch);
+		break;
+	case TAS25XX_DEFAULT:
+		dev_info(plat_data->dev, "%s:[default]  ch-%d %s\n", __func__, ch,
+				RX_SLOTS[(int32_t)s_rbin.def_hw_params.rx_bitwidth]);
+		ret = tas25xx_process_block(p_tas25xx,
+			s_rbin.hw_params[ch].RX_BITWIDTH[(int32_t)s_rbin.def_hw_params.rx_bitwidth], ch);
+		break;
+	default:
+		dev_info(plat_data->dev, "%s: ch-%d Invalid RX_BITWIDTH %d\n", __func__, ch,
+				rx_bitwidth);
+		break;
+	}
+	return ret;
+}
+
+int32_t tas25xx_set_rx_slotlen(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t rx_slotlen)
+{
+	int32_t ret = -1;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	switch (rx_slotlen) {
+	case RX_SLOTLEN_16:
+		dev_info(plat_data->dev, "%s: ch-%d RX_SLOTLEN_16\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].RX_SLOTLEN[RX_SLOTLEN_16], ch);
+		break;
+	case RX_SLOTLEN_24:
+		dev_info(plat_data->dev, "%s: ch-%d RX_SLOTLEN_24\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].RX_SLOTLEN[RX_SLOTLEN_24], ch);
+		break;
+	case RX_SLOTLEN_32:
+		dev_info(plat_data->dev, "%s: ch-%d RX_SLOTLEN_32\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].RX_SLOTLEN[RX_SLOTLEN_32], ch);
+		break;
+	case TAS25XX_DEFAULT:
+		dev_info(plat_data->dev, "%s:[default]  ch-%d %s\n", __func__, ch,
+				RX_SLOTS[(int32_t)s_rbin.def_hw_params.rx_slotlen]);
+		ret = tas25xx_process_block(p_tas25xx,
+			s_rbin.hw_params[ch].RX_SLOTLEN[(int32_t)s_rbin.def_hw_params.rx_slotlen], ch);
+		break;
+	default:
+		dev_info(plat_data->dev, "%s: ch-%d Invalid RX_SLOTLEN %d\n", __func__, ch,
+				rx_slotlen);
+		break;
+	}
+	return ret;
+}
+
+int32_t tas25xx_set_tx_slotlen(struct tas25xx_priv *p_tas25xx, int32_t ch, int32_t tx_slotlen)
+{
+	int32_t ret = -1;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	switch (tx_slotlen) {
+	case TX_SLOTLEN_16:
+		dev_info(plat_data->dev, "%s: ch-%d TX_SLOTLEN_16\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].TX_SLOTLEN[TX_SLOTLEN_16], ch);
+		break;
+	case TX_SLOTLEN_24:
+		dev_info(plat_data->dev, "%s: ch-%d TX_SLOTLEN_24\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].TX_SLOTLEN[TX_SLOTLEN_24], ch);
+		break;
+	case TX_SLOTLEN_32:
+		dev_info(plat_data->dev, "%s: ch-%d TX_SLOTLEN_32\n", __func__, ch);
+		ret = tas25xx_process_block(p_tas25xx, s_rbin.hw_params[ch].TX_SLOTLEN[TX_SLOTLEN_32], ch);
+		break;
+	case TAS25XX_DEFAULT:
+		dev_info(plat_data->dev, "%s:[default] ch-%d %s\n", __func__, ch,
+				RX_SLOTS[(int32_t)s_rbin.def_hw_params.tx_slotlen]);
+		ret = tas25xx_process_block(p_tas25xx,
+			s_rbin.hw_params[ch].TX_SLOTLEN[(int32_t)s_rbin.def_hw_params.tx_slotlen], ch);
+		break;
+	default:
+		dev_info(plat_data->dev, "%s: ch-%d Invalid TX_SLOTLEN %d\n", __func__, ch,
+				tx_slotlen);
+		break;
+	}
+	return ret;
+}
+
+int32_t tas25xx_set_pre_powerup(struct tas25xx_priv *p_tas25xx, int32_t ch)
+{
+	int32_t ret = 0;
+	struct linux_platform *plat_data = NULL;
+	struct tas25xx_profiles *tas_profiles_t =
+		(struct tas25xx_profiles *)g_profile_data_list[ch].tas_profiles;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	dev_info(plat_data->dev, "ch-%d %s profile %s pre_power_up\n", ch, __func__,
+			tas_profiles_t[g_tas25xx_profile].name);
+	ret = tas25xx_process_block(p_tas25xx, tas_profiles_t[g_tas25xx_profile].pre_power_up, ch);
+	return ret;
+}
+
+int32_t tas25xx_set_post_powerup(struct tas25xx_priv *p_tas25xx, int32_t ch)
+{
+	int32_t ret = -1;
+	struct linux_platform *plat_data = NULL;
+	struct tas25xx_profiles *tas_profiles_t =
+		(struct tas25xx_profiles *)g_profile_data_list[ch].tas_profiles;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	dev_info(plat_data->dev, "ch-%d %s profile %s post_power_up\n", ch,
+		__func__, tas_profiles_t[g_tas25xx_profile].name);
+	ret = tas25xx_process_block(p_tas25xx, tas_profiles_t[g_tas25xx_profile].post_power_up, ch);
+	return ret;
+}
+
+int32_t tas25xx_set_pre_powerdown(struct tas25xx_priv *p_tas25xx, int32_t ch)
+{
+	int32_t ret = -1;
+	struct linux_platform *plat_data = NULL;
+	struct tas25xx_profiles *tas_profiles_t = (struct tas25xx_profiles *)g_profile_data_list[ch].tas_profiles;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	dev_info(plat_data->dev, "ch-%d %s profile %s pre_power_down\n",
+		ch, __func__, tas_profiles_t[g_tas25xx_profile].name);
+	ret = tas25xx_process_block(p_tas25xx, tas_profiles_t[g_tas25xx_profile].pre_power_down, ch);
+	return ret;
+}
+
+int32_t tas25xx_set_post_powerdown(struct tas25xx_priv *p_tas25xx, int32_t ch)
+{
+	int32_t ret = -1;
+	struct linux_platform *plat_data = NULL;
+	struct tas25xx_profiles *tas_profiles_t = (struct tas25xx_profiles *)g_profile_data_list[ch].tas_profiles;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	dev_info(plat_data->dev, "ch-%d %s profile %s post_power_down\n",
+		ch, __func__, tas_profiles_t[g_tas25xx_profile].name);
+	ret = tas25xx_process_block(p_tas25xx, tas_profiles_t[g_tas25xx_profile].post_power_down, ch);
+	return ret;
+}
+
+int32_t tas25xx_remove_binfile(struct tas25xx_priv *p_tas25xx)
+{
+	int32_t i = 0, j = 0;
+	int32_t count;
+
+	/* firmware not loaded */
+	if (p_tas25xx)
+		atomic_set(&p_tas25xx->fw_state, TAS25XX_DSP_FW_NONE);
+
+	if (g_profile_list) {
+		for (i = 0; i < g_no_of_profiles; i++) {
+			kfree(g_profile_list[i]);
+			g_profile_list[i] = NULL;
+		}
+		kfree(g_profile_list);
+		g_profile_list = NULL;
+	}
+
+	for (i = 0; i < s_rbin.head.channels; i++) {
+		kfree(g_profile_data_list[i].tas_profiles);
+		g_profile_data_list[i].tas_profiles = NULL;
+	}
+
+	if (p_tas25xx) {
+		for (i = 0; i < s_rbin.head.channels; i++) {
+			kfree(p_tas25xx->intr_data[i].intr_info);
+			p_tas25xx->intr_data[i].intr_info = NULL;
+		}
+	}
+
+	if (g_kctrl_data) {
+		for (i = 0; i < g_no_of_kcontrols; i++) {
+			if (g_kctrl_data[i].type != 0) {
+				count = g_kctrl_data[i].kcontrol.enum_type.count;
+				for (j = 0; j < count; j++) {
+					kfree(g_kctrl_data[i].kcontrol.enum_type.enum_texts[j]);
+					g_kctrl_data[i].kcontrol.enum_type.enum_texts[j] = NULL;
+				}
+				kfree(g_kctrl_data[i].kcontrol.enum_type.enum_texts);
+				g_kctrl_data[i].kcontrol.enum_type.enum_texts = NULL;
+
+				kfree(g_kctrl_data[i].kcontrol.enum_type.data);
+				g_kctrl_data[i].kcontrol.enum_type.data = NULL;
+			} else {
+				kfree(g_kctrl_data[i].kcontrol.int_type.intdata);
+				g_kctrl_data[i].kcontrol.int_type.intdata = NULL;
+				kfree(g_kctrl_data[i].kcontrol.int_type.chardata);
+				g_kctrl_data[i].kcontrol.int_type.chardata = NULL;
+			}
+		}
+
+		kfree(g_kctrl_data);
+		g_kctrl_data = NULL;
+	}
+
+	kfree(g_kctrl_ctrl);
+	g_kctrl_ctrl = NULL;
+
+	kfree(p_tas25xx->fw_data);
+	p_tas25xx->fw_data = NULL;
+
+	g_no_of_kcontrols = 0;
+	g_no_of_profiles = 0;
+	g_tas25xx_profile = 0;
+	memset(&s_rbin, 0, sizeof(s_rbin));
+
+	g_tas25xx = NULL;
+
+	return 0;
+}

+ 1121 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/src/tas25xx-regmap.c

@@ -0,0 +1,1121 @@
+/*
+ * ALSA SoC Texas Instruments TAS25XX High Performance 4W Smart Amplifier
+ *
+ * Copyright (C) 2016 Texas Instruments, Inc.
+ *
+ * Author: Niranjan H Y, saiprasad
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+//#define DEBUG 5
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pm.h>
+#include <linux/version.h>
+#include "../inc/tas25xx-ext.h"
+#include "../inc/tas25xx.h"
+#include "../inc/tas25xx-logic.h"
+#include "../inc/tas25xx-device.h"
+#include "../inc/tas25xx-codec.h"
+#include "../inc/tas25xx-regmap.h"
+#include "../inc/tas25xx-regbin-parser.h"
+#include "../inc/tas25xx-misc.h"
+#include <linux/kobject.h>
+#if IS_ENABLED(CONFIG_TAS25XX_ALGO)
+#include "../algo/inc/tas_smart_amp_v2.h"
+#endif /*CONFIG_TAS25XX_ALGO*/
+/*For mixer_control implementation*/
+#define MAX_STRING	200
+#define SYS_NODE
+
+#define CHK_ONE_BIT_SET(x) (((x) & ((x)-1)) == 0)
+
+static const char *dts_tag[][2] = {
+	{
+		"ti,reset-gpio",
+		"ti,irq-gpio",
+	},
+	{
+		"ti,reset-gpio2",
+		"ti,irq-gpio2"
+	},
+	{
+		"ti,reset-gpio3",
+		"ti,irq-gpio3",
+	},
+	{
+		"ti,reset-gpio4",
+		"ti,irq-gpio4"
+	}
+};
+
+static const char *reset_gpio_label[2] = {
+	"TAS25XX_RESET", "TAS25XX_RESET2"
+};
+
+static int ti_amp_state[MAX_CHANNELS];
+int tas25xx_get_state(uint32_t id)
+{
+	if (id >= MAX_CHANNELS) {
+		pr_err("tas25xx: invalid amp id %d\n", id);
+		return TAS_AMP_ERR_EINVAL;
+	}
+
+	return ti_amp_state[id];
+}
+EXPORT_SYMBOL_GPL(tas25xx_get_state);
+
+static void (*tas_i2c_err_fptr)(uint32_t);
+
+void tas25xx_register_i2c_error_callback(void (*i2c_err_cb)(uint32_t))
+{
+	tas_i2c_err_fptr = i2c_err_cb;
+}
+EXPORT_SYMBOL_GPL(tas25xx_register_i2c_error_callback);
+
+static int s_last_i2c_error;
+
+static inline void tas25xx_post_i2c_err_to_platform(uint32_t i2caddr)
+{
+	pr_err("tas25xx: I2C err on 0x%2x, notify on cb=%d\n",
+		i2caddr, tas_i2c_err_fptr ? 1 : 0);
+	if (tas_i2c_err_fptr)
+		tas_i2c_err_fptr(i2caddr);
+}
+
+int tas25xx_check_last_i2c_error_n_reset(void)
+{
+	int temp = s_last_i2c_error;
+	s_last_i2c_error = 0;
+	return temp;
+}
+
+static int tas25xx_regmap_write(void *plat_data, uint32_t i2c_addr,
+	uint32_t reg, uint32_t value, uint32_t channel)
+{
+	int ret;
+	int retry_count = TAS25XX_I2C_RETRY_COUNT;
+	struct linux_platform *platform_data =
+		(struct linux_platform *)plat_data;
+
+	platform_data->client->addr = i2c_addr;
+	while (retry_count > 0) {
+		ret = regmap_write(platform_data->regmap[channel], reg,
+			value);
+		if (!ret)
+			break;
+		tas25xx_post_i2c_err_to_platform(i2c_addr);
+		mdelay(20);
+		retry_count--;
+	}
+
+	if (ret)
+		s_last_i2c_error = ret;
+
+	return ret;
+}
+
+static int tas25xx_regmap_bulk_write(void *plat_data, uint32_t i2c_addr,
+	uint32_t reg, uint8_t *pData,
+	uint32_t nLength, uint32_t channel)
+{
+	int ret;
+	int retry_count = TAS25XX_I2C_RETRY_COUNT;
+	struct linux_platform *platform_data =
+		(struct linux_platform *)plat_data;
+
+	platform_data->client->addr = i2c_addr;
+	while (retry_count > 0) {
+		ret = regmap_bulk_write(platform_data->regmap[channel], reg,
+			 pData, nLength);
+		if (!ret)
+			break;
+		tas25xx_post_i2c_err_to_platform(i2c_addr);
+		mdelay(20);
+		retry_count--;
+	}
+
+	if (ret)
+		s_last_i2c_error = ret;
+
+	return ret;
+}
+
+static int tas25xx_regmap_read(void *plat_data, uint32_t i2c_addr,
+	uint32_t reg, uint32_t *value, uint32_t channel)
+{
+	int ret;
+	int retry_count = TAS25XX_I2C_RETRY_COUNT;
+	struct linux_platform *platform_data =
+		(struct linux_platform *)plat_data;
+
+	platform_data->client->addr = i2c_addr;
+	while (retry_count > 0) {
+		ret = regmap_read(platform_data->regmap[channel], reg,
+			value);
+		if (!ret)
+			break;
+		tas25xx_post_i2c_err_to_platform(i2c_addr);
+		mdelay(20);
+		retry_count--;
+	}
+
+	if (ret)
+		s_last_i2c_error = ret;
+
+	return ret;
+}
+
+static int tas25xx_regmap_bulk_read(void *plat_data, uint32_t i2c_addr,
+	uint32_t reg, uint8_t *pData,
+	uint32_t nLength, uint32_t channel)
+{
+	int ret;
+	int retry_count = TAS25XX_I2C_RETRY_COUNT;
+	struct linux_platform *platform_data =
+		(struct linux_platform *)plat_data;
+
+	platform_data->client->addr = i2c_addr;
+	while (retry_count > 0) {
+		ret = regmap_bulk_read(platform_data->regmap[channel], reg,
+			 pData, nLength);
+		if (!ret)
+			break;
+		tas25xx_post_i2c_err_to_platform(i2c_addr);
+		mdelay(20);
+		retry_count--;
+	}
+
+	if (ret)
+		s_last_i2c_error = ret;
+
+	return ret;
+}
+
+static int tas25xx_regmap_update_bits(void *plat_data, uint32_t i2c_addr,
+	uint32_t reg, uint32_t mask,
+	uint32_t value, uint32_t channel)
+{
+	int ret;
+	int retry_count = TAS25XX_I2C_RETRY_COUNT;
+	struct linux_platform *platform_data =
+		(struct linux_platform *)plat_data;
+
+	platform_data->client->addr = i2c_addr;
+	while (retry_count > 0) {
+		ret = regmap_update_bits(platform_data->regmap[channel], reg,
+			mask, value);
+		if (!ret)
+			break;
+		tas25xx_post_i2c_err_to_platform(i2c_addr);
+		mdelay(20);
+		retry_count--;
+	}
+
+	if (ret)
+		s_last_i2c_error = ret;
+
+	return ret;
+}
+
+static int tas25xx_dev_read(struct tas25xx_priv *p_tas25xx,
+	int32_t chn, uint32_t reg, uint32_t *pValue)
+{
+	int ret = -EINVAL;
+
+	mutex_lock(&p_tas25xx->dev_lock);
+
+	/* Switch book */
+	if (TAS25XX_BOOK_ID(reg)) {
+		ret = tas25xx_change_book(p_tas25xx, chn,
+			TAS25XX_BOOK_ID(reg));
+		if (ret)
+			goto end;
+	}
+
+	ret = p_tas25xx->plat_read(p_tas25xx->platform_data,
+			p_tas25xx->devs[chn]->mn_addr,
+			TAS25XX_REG_NO_BOOK(reg), pValue, chn);
+	if (ret) {
+		pr_err("%s, ERROR, L=%d, E=%d\n",
+			__func__, __LINE__, ret);
+	} else {
+		pr_debug(
+			"%s: chn:%x:BOOK:PAGE:REG 0x%02x:0x%02x:0x%02x,0x%02x\n",
+			__func__,
+			p_tas25xx->devs[chn]->mn_addr, TAS25XX_BOOK_ID(reg),
+			TAS25XX_PAGE_ID(reg),
+			TAS25XX_PAGE_REG(reg), *pValue);
+	}
+
+	/* Switch to Book-0 */
+	if (TAS25XX_BOOK_ID(reg))
+		ret = tas25xx_change_book(p_tas25xx, chn, 0);
+
+end:
+	mutex_unlock(&p_tas25xx->dev_lock);
+	return ret;
+}
+
+static int tas25xx_dev_write(struct tas25xx_priv *p_tas25xx, int32_t chn,
+	uint32_t reg, uint32_t value)
+{
+	int ret = -EINVAL;
+
+	mutex_lock(&p_tas25xx->dev_lock);
+
+	/* Switch book */
+	if (TAS25XX_BOOK_ID(reg)) {
+		ret = tas25xx_change_book(p_tas25xx, chn,
+			TAS25XX_BOOK_ID(reg));
+		if (ret)
+			goto end;
+	}
+
+	ret = p_tas25xx->plat_write(
+			p_tas25xx->platform_data,
+			p_tas25xx->devs[chn]->mn_addr,
+			TAS25XX_REG_NO_BOOK(reg), value, chn);
+	if (ret) {
+		pr_err(
+			"%s, ERROR, L=%u, chn=0x%02x, E=%d\n",
+			__func__, __LINE__,
+			p_tas25xx->devs[chn]->mn_addr, ret);
+	} else {
+		pr_debug(
+			"%s: %u: chn:0x%02x:BOOK:PAGE:REG 0x%02x:0x%02x:0x%02x, VAL: 0x%02x\n",
+			__func__, __LINE__,
+			p_tas25xx->devs[chn]->mn_addr,
+			TAS25XX_BOOK_ID(reg),
+			TAS25XX_PAGE_ID(reg),
+			TAS25XX_PAGE_REG(reg), value);
+
+	}
+
+	/* Switch book to 0 */
+	if (TAS25XX_BOOK_ID(reg)) {
+		ret = tas25xx_change_book(p_tas25xx, chn, 0);
+		if (ret)
+			goto end;
+	}
+
+end:
+	mutex_unlock(&p_tas25xx->dev_lock);
+	return ret;
+}
+
+static int tas25xx_dev_bulk_write(struct tas25xx_priv *p_tas25xx,
+	int32_t chn, uint32_t reg, uint8_t *p_data, uint32_t n_length)
+{
+	int ret = -EINVAL;
+
+	mutex_lock(&p_tas25xx->dev_lock);
+
+	/* Switch book */
+	if (TAS25XX_BOOK_ID(reg)) {
+		ret = tas25xx_change_book(p_tas25xx, chn,
+			TAS25XX_BOOK_ID(reg));
+		if (ret)
+			goto end;
+	}
+
+	ret = p_tas25xx->plat_bulk_write(
+			p_tas25xx->platform_data,
+			p_tas25xx->devs[chn]->mn_addr,
+			TAS25XX_REG_NO_BOOK(reg),
+			p_data, n_length, chn);
+	if (ret) {
+		pr_err(
+			"%s, ERROR, L=%u, chn=0x%02x: E=%d\n",
+			__func__, __LINE__,
+			p_tas25xx->devs[chn]->mn_addr, ret);
+	} else {
+		pr_debug(
+			"%s: chn%x:BOOK:PAGE:REG 0x%02x:0x%02x:0x%02x, len: %u\n",
+			__func__, p_tas25xx->devs[chn]->mn_addr,
+			TAS25XX_BOOK_ID(reg),
+			TAS25XX_PAGE_ID(reg),
+			TAS25XX_PAGE_REG(reg), n_length);
+	}
+
+	/* Switch book to 0*/
+	if (TAS25XX_BOOK_ID(reg)) {
+		ret = tas25xx_change_book(p_tas25xx, chn, 0);
+		if (ret)
+			goto end;
+	}
+
+end:
+	mutex_unlock(&p_tas25xx->dev_lock);
+	return ret;
+}
+
+static int tas25xx_dev_bulk_read(struct tas25xx_priv *p_tas25xx,
+	int32_t chn, uint32_t reg, uint8_t *p_data, uint32_t n_length)
+{
+	int ret;
+
+	mutex_lock(&p_tas25xx->dev_lock);
+
+	/* Switch book */
+	if (TAS25XX_BOOK_ID(reg)) {
+		ret = tas25xx_change_book(p_tas25xx, chn,
+			TAS25XX_BOOK_ID(reg));
+		if (ret)
+			goto end;
+	}
+
+	ret = p_tas25xx->plat_bulk_read(p_tas25xx->platform_data,
+		p_tas25xx->devs[chn]->mn_addr, TAS25XX_REG_NO_BOOK(reg),
+		p_data, n_length, chn);
+	if (ret) {
+		pr_err("%s, ERROR, L=%d, E=%d\n", __func__, __LINE__, ret);
+	} else {
+		pr_debug(
+			"%s: chn%x:BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n",
+			__func__, p_tas25xx->devs[chn]->mn_addr,
+			TAS25XX_BOOK_ID(reg), TAS25XX_PAGE_ID(reg),
+			TAS25XX_PAGE_REG(reg), n_length);
+	}
+
+	/* Switch book to 0 */
+	if (TAS25XX_BOOK_ID(reg)) {
+		ret = tas25xx_change_book(p_tas25xx, chn, 0);
+		if (ret)
+			goto end;
+	}
+
+end:
+	mutex_unlock(&p_tas25xx->dev_lock);
+	return ret;
+}
+
+static int tas25xx_dev_update_bits(struct tas25xx_priv *p_tas25xx,
+	int32_t chn, uint32_t reg, uint32_t mask, uint32_t value)
+{
+	int ret;
+
+	mutex_lock(&p_tas25xx->dev_lock);
+
+	/* Switch book */
+	if (TAS25XX_BOOK_ID(reg)) {
+		ret = tas25xx_change_book(p_tas25xx, chn,
+			TAS25XX_BOOK_ID(reg));
+		if (ret)
+			goto end;
+	}
+
+	ret = p_tas25xx->plat_update_bits(
+			p_tas25xx->platform_data,
+			p_tas25xx->devs[chn]->mn_addr,
+			TAS25XX_REG_NO_BOOK(reg),
+			mask, value, chn);
+	if (ret) {
+		pr_err("%s, ERROR, L=%u, chn=0x%02x: E=%d\n", __func__, __LINE__,
+			p_tas25xx->devs[chn]->mn_addr, ret);
+	} else {
+		pr_debug("%s: chn%x:BOOK:PAGE:REG 0x%02x:0x%02x:0x%02x, mask: 0x%02x, val: 0x%02x\n",
+			__func__, p_tas25xx->devs[chn]->mn_addr,
+				TAS25XX_BOOK_ID(reg), TAS25XX_PAGE_ID(reg), TAS25XX_PAGE_REG(reg), mask, value);
+	}
+
+	/* Switch book to 0*/
+	if (TAS25XX_BOOK_ID(reg)) {
+		ret = tas25xx_change_book(p_tas25xx, chn, 0);
+		if (ret)
+			goto end;
+	}
+
+end:
+	mutex_unlock(&p_tas25xx->dev_lock);
+	return ret;
+}
+
+/*
+ * consider all the registers as volatile,
+ * this list is not known to the driver at the time of regmap init
+ */
+static bool tas25xx_volatile(struct device *dev, unsigned int reg)
+{
+	return true;
+}
+
+/*
+ * consider all the registers as writable as
+ * this list is not known to the driver at the time of regmap init
+ */
+static bool tas25xx_writeable(struct device *dev, unsigned int reg)
+{
+	return true;
+}
+
+struct regmap_range_cfg tas25xx_ranges[] = {
+	{
+		.name = "tas25xx range",
+		.range_min = 0,
+		.range_max = 256 * 128,
+		.selector_reg = TAS25XX_PAGECTL_REG,
+		.selector_mask = 0xFF,
+		.selector_shift = 0,
+		.window_start = 0,
+		.window_len = 128,
+	},
+};
+
+static const struct reg_default tas25xx_reg_defaults[] = {
+	{ TAS25XX_PAGECTL_REG, 0x00 },
+};
+
+static const struct regmap_config tas25xx_i2c_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.writeable_reg = tas25xx_writeable,
+	.volatile_reg = tas25xx_volatile,
+	.reg_defaults = tas25xx_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(tas25xx_reg_defaults),
+	.max_register = 256 * 128,
+	.ranges = tas25xx_ranges,
+	.num_ranges = ARRAY_SIZE(tas25xx_ranges),
+	.cache_type = REGCACHE_RBTREE,
+};
+
+#define TEST_HW_RESET 0
+
+static void tas25xx_hw_reset(struct tas25xx_priv *p_tas25xx)
+{
+	struct linux_platform *plat_data = NULL;
+	int i = 0;
+#if TEST_HW_RESET
+	uint32_t regval_b = 0;
+	uint32_t regval_c = 0;
+	uint32_t regval_d = 0;
+#endif
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	dev_info(plat_data->dev, "Before HW reset\n");
+#if TEST_HW_RESET
+	p_tas25xx->update_bits(p_tas25xx, 0, 0xb, 0x40, 0x40);
+	p_tas25xx->update_bits(p_tas25xx, 0, 0xb, 0x3F, 0x07);
+
+	p_tas25xx->update_bits(p_tas25xx, 0, 0xc, 0x40, 0x40);
+	p_tas25xx->update_bits(p_tas25xx, 0, 0xc, 0x3F, 0x08);
+
+	p_tas25xx->update_bits(p_tas25xx, 0, 0xd, 0x40, 0x40);
+	p_tas25xx->update_bits(p_tas25xx, 0, 0xd, 0x3F, 0x09);
+
+	dev_info(plat_data->dev, "----START----\n");
+
+	p_tas25xx->read(p_tas25xx, 0, 0xb, &regval_b);
+	p_tas25xx->read(p_tas25xx, 0, 0xc, &regval_c);
+	p_tas25xx->read(p_tas25xx, 0, 0xd, &regval_d);
+
+	dev_info(plat_data->dev, "%02x %02x %02x",
+		regval_b, regval_c, regval_d);
+#endif
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		if (gpio_is_valid(p_tas25xx->devs[i]->reset_gpio)) {
+			dev_info(plat_data->dev, "GPIO is valid %d\n",
+				p_tas25xx->devs[i]->reset_gpio);
+			gpio_direction_output(p_tas25xx->devs[i]->reset_gpio, 0);
+		} else {
+			dev_dbg(plat_data->dev, "GPIO is not valid %d\n",
+				p_tas25xx->devs[i]->reset_gpio);
+		}
+	}
+	msleep(20);
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		if (gpio_is_valid(p_tas25xx->devs[i]->reset_gpio))
+			gpio_direction_output(p_tas25xx->devs[i]->reset_gpio, 1);
+		p_tas25xx->devs[i]->mn_current_book = -1;
+	}
+	usleep_range(10000, 10100);
+
+	dev_info(plat_data->dev, "After HW reset\n");
+
+#if TEST_HW_RESET
+	p_tas25xx->read(p_tas25xx, 0, 0xb, &regval_b);
+	p_tas25xx->read(p_tas25xx, 0, 0xc, &regval_c);
+	p_tas25xx->read(p_tas25xx, 0, 0xd, &regval_d);
+
+	dev_info(plat_data->dev, "%02x %02x %02x",
+		regval_b, regval_c, regval_d);
+	dev_info(plat_data->dev, "---- END ----\n");
+#endif
+
+	dev_info(plat_data->dev, "%s exit\n", __func__);
+}
+
+static void tas25xx_enable_irq(struct tas25xx_priv *p_tas25xx)
+{
+	struct irq_desc *desc = NULL;
+	struct linux_platform *plat_data = NULL;
+	int i = 0;
+	int irq_no;
+	int irq_gpio;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		irq_gpio = p_tas25xx->devs[i]->irq_gpio;
+		if (!gpio_is_valid(irq_gpio))
+			continue;
+
+		if (p_tas25xx->irq_enabled[i] == 0) {
+			irq_no = p_tas25xx->devs[i]->irq_no;
+			desc = irq_data_to_desc(irq_get_irq_data(irq_no));
+			if (desc && desc->depth > 0) {
+				enable_irq(irq_no);
+				dev_info(plat_data->dev, "irq_no=%d(gpio=%d), enabled irq",
+					irq_no, irq_gpio);
+				p_tas25xx->irq_enabled[i] = 1;
+			} else if (desc) {
+				p_tas25xx->irq_enabled[i] = 1;
+				dev_info(plat_data->dev, "irq_no=%d already enabled", irq_no);
+			} else {
+				dev_info(plat_data->dev, "failed to get descritptor");
+			}
+		} else {
+			dev_info(plat_data->dev, "GPIO %d, previous IRQ status=%s",
+			irq_gpio, p_tas25xx->irq_enabled[i] ? "Enabled" : "Disabled");
+		}
+	}
+}
+
+static void tas25xx_disable_irq(struct tas25xx_priv *p_tas25xx)
+{
+	int i = 0;
+	int irq_gpio;
+	int irq_no;
+
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		irq_gpio = p_tas25xx->devs[i]->irq_gpio;
+		irq_no = p_tas25xx->devs[i]->irq_no;
+		if (!gpio_is_valid(irq_gpio))
+			continue;
+		if (p_tas25xx->irq_enabled[i] == 1) {
+			dev_info(plat_data->dev, "irq_no=%d(gpio=%d), disabling IRQ", irq_no, irq_gpio);
+			disable_irq_nosync(irq_no);
+			p_tas25xx->irq_enabled[i] = 0;
+		}
+	}
+}
+
+static void irq_work_routine(struct work_struct *work)
+{
+	struct tas25xx_priv *p_tas25xx =
+		container_of(work, struct tas25xx_priv, irq_work.work);
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+	dev_info(plat_data->dev, "%s\n", __func__);
+
+	/* Codec Lock Hold*/
+	mutex_lock(&p_tas25xx->codec_lock);
+
+	if (plat_data->mb_runtime_suspend) {
+		pr_info("%s, Runtime Suspended\n", __func__);
+	} else {
+		/*Logical Layer IRQ function, return is ignored*/
+		tas25xx_irq_work_func(p_tas25xx);
+	}
+
+	/* Codec Lock Release*/
+	mutex_unlock(&p_tas25xx->codec_lock);
+
+	return;
+}
+
+static void init_work_routine(struct work_struct *work)
+{
+	int ret, chn;
+	struct tas_device *dev_tas25xx =
+		container_of(work, struct tas_device, init_work.work);
+	struct tas25xx_priv *p_tas25xx = dev_tas25xx->prv_data;
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+	chn = dev_tas25xx->channel_no;
+
+	mutex_lock(&p_tas25xx->codec_lock);
+
+	if (p_tas25xx->m_power_state == TAS_POWER_ACTIVE) {
+		tas25xx_init_work_func(p_tas25xx, dev_tas25xx);
+		ret = tas25xx_update_kcontrol_data(p_tas25xx,
+			KCNTR_POST_POWERUP, 1 << chn);
+		if (ret)
+			dev_err(plat_data->dev,
+				"%s error during post powerup kcontrol update", __func__);
+	} else {
+		dev_info(plat_data->dev,
+			"skipping init work as the device state was changed");
+	}
+
+	mutex_unlock(&p_tas25xx->codec_lock);
+}
+
+static int tas25xx_runtime_suspend(struct tas25xx_priv *p_tas25xx)
+{
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+	dev_dbg(plat_data->dev, "%s\n", __func__);
+
+	plat_data->mb_runtime_suspend = true;
+
+	if (delayed_work_pending(&p_tas25xx->irq_work)) {
+		dev_dbg(plat_data->dev, "cancel IRQ work\n");
+		cancel_delayed_work(&p_tas25xx->irq_work);
+	}
+
+	return 0;
+}
+
+static int tas25xx_runtime_resume(struct tas25xx_priv *p_tas25xx)
+{
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+	dev_dbg(plat_data->dev, "%s\n", __func__);
+
+	plat_data->mb_runtime_suspend = false;
+
+	return 0;
+}
+
+static int tas25xx_pm_suspend(struct device *dev)
+{
+	struct tas25xx_priv *p_tas25xx = dev_get_drvdata(dev);
+	struct linux_platform *plat_data = NULL;
+	int i = 0;
+
+	if (!p_tas25xx) {
+		pr_err("tas25xx:%s drvdata is NULL\n", __func__);
+		return -EINVAL;
+	}
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+	if (!plat_data) {
+		pr_err("tas25xx:%s plat_data is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Codec Lock Hold*/
+	mutex_lock(&p_tas25xx->codec_lock);
+	plat_data->i2c_suspend = true;
+	/* Only cache register writes and no actual I2C writes */
+	for (i = 0; i < p_tas25xx->ch_count; i++)
+		regcache_cache_only(plat_data->regmap[i], true);
+	tas25xx_runtime_suspend(p_tas25xx);
+	/* Codec Lock Release*/
+	mutex_unlock(&p_tas25xx->codec_lock);
+	return 0;
+}
+
+static int tas25xx_pm_resume(struct device *dev)
+{
+	struct tas25xx_priv *p_tas25xx = dev_get_drvdata(dev);
+	struct linux_platform *plat_data = NULL;
+	int i = 0;
+
+	if (!p_tas25xx) {
+		pr_err("tas25xx:%s drvdata is NULL\n", __func__);
+		return -EINVAL;
+	}
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+	if (!plat_data) {
+		pr_err("tas25xx:%s plat_data is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Codec Lock Hold*/
+	mutex_lock(&p_tas25xx->codec_lock);
+	plat_data->i2c_suspend = false;
+
+	/* Restore all I2C writes happend during supended state */
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		regcache_cache_only(plat_data->regmap[i], false);
+		regcache_sync(plat_data->regmap[i]);
+	}
+
+	tas25xx_runtime_resume(p_tas25xx);
+	/* Codec Lock Release*/
+	mutex_unlock(&p_tas25xx->codec_lock);
+	return 0;
+}
+
+
+void schedule_init_work(struct tas25xx_priv *p_tas25xx, int ch)
+{
+	/* actual init work will decide the delay */
+	schedule_delayed_work(&p_tas25xx->devs[ch]->init_work,
+		msecs_to_jiffies(0));
+}
+
+void cancel_init_work(struct tas25xx_priv *p_tas25xx, int ch)
+{
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+	cancel_delayed_work(&p_tas25xx->devs[ch]->init_work);
+
+}
+
+static int tas25xx_parse_dt(struct device *dev,
+				struct tas25xx_priv *p_tas25xx)
+{
+
+	struct device_node *np = dev->of_node;
+	struct linux_platform *plat_data = NULL;
+	int rc = 0, i = 0;
+	uint32_t reset_gpios = 0;
+	uint32_t irq_gpios = 0;
+	uint8_t buf[32];
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+
+	rc = of_property_read_u32(np, "ti,max-channels", &p_tas25xx->ch_count);
+	if (rc) {
+		dev_err(plat_data->dev,
+			"Looking up %s property in node %s, err=%d\n, using default=%d",
+			"ti,max-channels", np->full_name, rc, MAX_CHANNELS);
+		p_tas25xx->ch_count = MAX_CHANNELS;
+	} else {
+		dev_dbg(plat_data->dev, "ti,max-channels=%d",
+			p_tas25xx->ch_count);
+	}
+
+	if (p_tas25xx->ch_count > MAX_CHANNELS) {
+		dev_err(plat_data->dev,
+			"ti,max-channels count exceeds limit(%d)", MAX_CHANNELS);
+		goto EXIT;
+	}
+
+	/*the device structures array*/
+	p_tas25xx->devs = kmalloc(p_tas25xx->ch_count * sizeof(struct tas_device *),
+		GFP_KERNEL);
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		p_tas25xx->devs[i] = kmalloc(sizeof(struct tas_device),
+					GFP_KERNEL);
+		if (!p_tas25xx->devs[i]) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		p_tas25xx->devs[i]->reset_gpio = of_get_named_gpio(np, dts_tag[i][0], 0);
+		if (!gpio_is_valid(p_tas25xx->devs[i]->reset_gpio)) {
+			dev_dbg(plat_data->dev, "Looking up %s property in node %s failed %d\n",
+				dts_tag[i][0], np->full_name, p_tas25xx->devs[i]->reset_gpio);
+		} else {
+			dev_info(plat_data->dev, "%s = %d", dts_tag[i][0],
+				p_tas25xx->devs[i]->reset_gpio);
+			reset_gpios |= 1 << i;
+		}
+
+		p_tas25xx->devs[i]->irq_gpio = of_get_named_gpio(np, dts_tag[i][1], 0);
+		if (!gpio_is_valid(p_tas25xx->devs[i]->irq_gpio)) {
+			dev_dbg(plat_data->dev,
+				"Looking up %s property in node %s failed %d\n",
+				dts_tag[i][1], np->full_name,
+				p_tas25xx->devs[i]->irq_gpio);
+		} else {
+			dev_info(plat_data->dev, "%s = %d", dts_tag[i][1],
+				p_tas25xx->devs[i]->irq_gpio);
+			irq_gpios |= 1 << i;
+		}
+	}
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		snprintf(buf, 32, "ti,channel-%d", i);
+		rc = of_property_read_u32(np, buf,
+			&p_tas25xx->devs[i]->mn_addr);
+		if (rc) {
+			dev_warn(plat_data->dev,
+				"Looking up %s property in node %s failed %d\n",
+				buf, np->full_name, rc);
+			p_tas25xx->devs[i]->mn_addr = -1;
+			rc = 0;
+		} else {
+			dev_info(plat_data->dev, "using %s = 0x%2x\n", buf,
+				p_tas25xx->devs[i]->mn_addr);
+		}
+	}
+
+	if (!irq_gpios)
+		dev_err(plat_data->dev, "IRQ GPIOs not found");
+	else if (CHK_ONE_BIT_SET(irq_gpios))
+		dev_info(plat_data->dev, "Using commong IRQ gpio");
+
+	if (!reset_gpios)
+		dev_err(plat_data->dev, "Reset GPIOs not found");
+	else if (CHK_ONE_BIT_SET(reset_gpios))
+		dev_info(plat_data->dev, "Using commong reset gpio");
+
+EXIT:
+	return rc;
+}
+
+static int tas25xx_i2c_probe(struct i2c_client *p_client,
+			const struct i2c_device_id *id)
+{
+	struct tas25xx_priv *p_tas25xx;
+	struct linux_platform *plat_data;
+	int ret = 0;
+	int i = 0;
+	int errcnt = 0;
+
+	dev_info(&p_client->dev, "Driver Tag: %s, addr=0x%2x\n",
+		TAS25XX_DRIVER_TAG, p_client->addr);
+
+	p_tas25xx = devm_kzalloc(&p_client->dev,
+		sizeof(struct tas25xx_priv), GFP_KERNEL);
+	if (p_tas25xx == NULL) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	plat_data = (struct linux_platform *)devm_kzalloc(&p_client->dev,
+		sizeof(struct linux_platform), GFP_KERNEL);
+	if (plat_data == NULL) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	p_tas25xx->platform_data = plat_data;
+	/* REGBIN related */
+	//p_tas25xx->profile_cfg_id = -1;
+
+	/* high level api */
+	p_tas25xx->read = tas25xx_dev_read;
+	p_tas25xx->write = tas25xx_dev_write;
+	p_tas25xx->bulk_read = tas25xx_dev_bulk_read;
+	p_tas25xx->bulk_write = tas25xx_dev_bulk_write;
+	p_tas25xx->update_bits = tas25xx_dev_update_bits;
+
+	/* low level api */
+	p_tas25xx->plat_write = tas25xx_regmap_write;
+	p_tas25xx->plat_read = tas25xx_regmap_read;
+	p_tas25xx->plat_bulk_write = tas25xx_regmap_bulk_write;
+	p_tas25xx->plat_bulk_read = tas25xx_regmap_bulk_read;
+	p_tas25xx->plat_update_bits = tas25xx_regmap_update_bits;
+
+	plat_data->client = p_client;
+	plat_data->dev = &p_client->dev;
+	i2c_set_clientdata(p_client, p_tas25xx);
+	dev_set_drvdata(&p_client->dev, p_tas25xx);
+
+	mutex_init(&p_tas25xx->dev_lock);
+	p_tas25xx->hw_reset = tas25xx_hw_reset;
+	p_tas25xx->enable_irq = tas25xx_enable_irq;
+	p_tas25xx->disable_irq = tas25xx_disable_irq;
+	p_tas25xx->schedule_init_work = schedule_init_work;
+	p_tas25xx->cancel_init_work = cancel_init_work;
+#if IS_ENABLED(CODEC_PM)
+	plat_data->runtime_suspend = tas25xx_runtime_suspend;
+	plat_data->runtime_resume = tas25xx_runtime_resume;
+#endif
+	p_tas25xx->m_power_state = TAS_POWER_SHUTDOWN;
+	p_tas25xx->ti_amp_state = ti_amp_state;
+
+	if (p_client->dev.of_node) {
+		ret = tas25xx_parse_dt(&p_client->dev, p_tas25xx);
+		if (ret)
+			goto err;
+	}
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		if (gpio_is_valid(p_tas25xx->devs[i]->reset_gpio)) {
+			ret = gpio_request(p_tas25xx->devs[i]->reset_gpio,
+					reset_gpio_label[i]);
+			if (ret) {
+				dev_err(plat_data->dev,
+					"%s: Failed to request gpio %d\n",
+					__func__,
+					p_tas25xx->devs[i]->reset_gpio);
+				ret = -EINVAL;
+				goto err;
+			} else {
+				/* make the gpio output always one */
+				dev_info(plat_data->dev, "%s setting reset gpio %d always high, default",
+					__func__, p_tas25xx->devs[i]->reset_gpio);
+				gpio_direction_output(p_tas25xx->devs[i]->reset_gpio, 1);
+			}
+
+		}
+		plat_data->regmap[i] = devm_regmap_init_i2c(p_client,
+			&tas25xx_i2c_regmap);
+		if (IS_ERR(plat_data->regmap[i])) {
+			ret = PTR_ERR(plat_data->regmap[i]);
+			dev_err(&p_client->dev,
+				"Failed to allocate register map: %d channel %d\n",
+				ret, i);
+			goto err;
+		}
+	}
+
+	ret = tas25xx_register_device(p_tas25xx);
+	if (ret)
+		goto err;
+
+	ret = 0;
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		if (p_tas25xx->devs[i]->mn_addr != -1) {
+			/* all i2c addressess registered must be accessible */
+			ret = p_tas25xx->read(p_tas25xx, i,
+				TAS25XX_REVID_REG, &p_tas25xx->dev_revid);
+			if (!ret) {
+				dev_info(&p_client->dev,
+					"successfully read revid 0x%x\n", p_tas25xx->dev_revid);
+				ti_amp_state[i] = TAS_AMP_STATE_BOOT_SUCCESS;
+			} else {
+				dev_err(&p_client->dev,
+					"Unable to read rev id, i2c failure\n");
+				ti_amp_state[i] = TAS_AMP_ERR_I2C;
+				errcnt++;
+			}
+		}
+	}
+
+	/* consider i2c error as fatal */
+	if (errcnt > 0) {
+		ret = -ENOENT;
+		goto err;
+	}
+
+	for (i = p_tas25xx->ch_count; i < MAX_CHANNELS; i++)
+		ti_amp_state[i] = TAS_AMP_ERR_EINVAL;
+
+	init_waitqueue_head(&p_tas25xx->fw_wait);
+
+	init_waitqueue_head(&p_tas25xx->dev_init_wait);
+	atomic_set(&p_tas25xx->dev_init_status, 0);
+
+	INIT_DELAYED_WORK(&p_tas25xx->irq_work, irq_work_routine);
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		p_tas25xx->devs[i]->prv_data = p_tas25xx;
+		INIT_DELAYED_WORK(&p_tas25xx->devs[i]->init_work,
+			init_work_routine);
+	}
+
+	mutex_init(&p_tas25xx->codec_lock);
+	ret = tas25xx_register_codec(p_tas25xx);
+	if (ret) {
+		dev_err(plat_data->dev,
+			"register codec failed, %d\n", ret);
+		goto err;
+	}
+
+	mutex_init(&p_tas25xx->file_lock);
+	ret = tas25xx_register_misc(p_tas25xx);
+	if (ret) {
+		dev_err(plat_data->dev, "register codec failed %d\n",
+			ret);
+		goto err;
+	}
+
+err:
+	return ret;
+}
+
+static void tas25xx_i2c_remove(struct i2c_client *p_client)
+{
+	int i = 0;
+	struct tas25xx_priv *p_tas25xx = i2c_get_clientdata(p_client);
+	struct linux_platform *plat_data = NULL;
+
+	plat_data = (struct linux_platform *) p_tas25xx->platform_data;
+	dev_info(plat_data->dev, "%s\n", __func__);
+
+	/*Cancel all the work routine before exiting*/
+	for (i = 0; i < p_tas25xx->ch_count; i++)
+		cancel_delayed_work_sync(&p_tas25xx->devs[i]->init_work);
+
+	cancel_delayed_work_sync(&p_tas25xx->irq_work);
+	cancel_delayed_work_sync(&p_tas25xx->dc_work);
+
+	tas25xx_deregister_codec(p_tas25xx);
+	mutex_destroy(&p_tas25xx->codec_lock);
+
+	tas25xx_deregister_misc(p_tas25xx);
+	mutex_destroy(&p_tas25xx->file_lock);
+
+	mutex_destroy(&p_tas25xx->dev_lock);
+
+	for (i = 0; i < p_tas25xx->ch_count; i++) {
+		if (gpio_is_valid(p_tas25xx->devs[i]->reset_gpio))
+			gpio_free(p_tas25xx->devs[i]->reset_gpio);
+		if (gpio_is_valid(p_tas25xx->devs[i]->irq_gpio))
+			gpio_free(p_tas25xx->devs[i]->irq_gpio);
+		kfree(p_tas25xx->devs[i]);
+	}
+
+	kfree(p_tas25xx->devs);
+}
+
+
+static const struct i2c_device_id tas25xx_i2c_id[] = {
+	{ "tas25xx", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, tas25xx_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tas25xx_of_match[] = {
+	{ .compatible = "ti,tas25xx" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, tas25xx_of_match);
+#endif
+
+static const struct dev_pm_ops tas25xx_pm_ops = {
+	.suspend = tas25xx_pm_suspend,
+	.resume = tas25xx_pm_resume
+};
+
+static struct i2c_driver tas25xx_i2c_driver = {
+	.driver = {
+		.name   = "tas25xx",
+		.owner  = THIS_MODULE,
+#if defined(CONFIG_OF)
+		.of_match_table = of_match_ptr(tas25xx_of_match),
+#endif
+		.pm = &tas25xx_pm_ops,
+	},
+	.probe      = tas25xx_i2c_probe,
+	.remove     = tas25xx_i2c_remove,
+	.id_table   = tas25xx_i2c_id,
+};
+
+module_i2c_driver(tas25xx_i2c_driver);
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS25XX I2C Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");

+ 212 - 0
qcom/opensource/audio-kernel/asoc/codecs/tas25xx/src/tas25xx.c

@@ -0,0 +1,212 @@
+/*
+ * ALSA SoC Texas Instruments TAS25XX High Performance 4W Smart Amplifier
+ *
+ * Copyright (C) 2022 Texas Instruments, Inc.
+ *
+ * Author: Niranjan H Y, Vijeth P O
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+#include "../inc/tas25xx.h"
+#include "../inc/tas25xx-device.h"
+#include "../inc/tas25xx-regbin-parser.h"
+#include "../inc/tas25xx-regmap.h"
+
+#define TAS25XX_MDELAY 0xFFFFFFFE
+#define TAS25XX_MSLEEP 0xFFFFFFFD
+#define TAS25XX_IVSENSER_ENABLE  1
+#define TAS25XX_IVSENSER_DISABLE 0
+
+#define STR_RXBITS_SZ 5
+#define STR_16BIT "16BIT"
+#define STR_24BIT "24BIT"
+#define	STR_32BIT "32BIT"
+
+#ifndef UINT64_MAX
+#define UINT64_MAX	((u64)(~((u64)0)))
+#endif
+
+#ifndef UINT32_MAX
+#define UINT32_MAX	((u32)(~((u32)0)))
+#endif
+
+int tas25xx_rx_set_bitwidth(struct tas25xx_priv *p_tas25xx,
+	int bitwidth, int ch)
+{
+	int ret;
+	int32_t i;
+	uint32_t size, blk_count, sublk_sz;
+	uint8_t *rx_data = p_tas25xx->block_op_data[ch].rx_fmt_data;
+	uint8_t *fmt;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+
+	ret = 0;
+	size = *((uint32_t *)rx_data);
+	rx_data += sizeof(uint32_t);
+	blk_count = *((uint32_t *)rx_data);
+	rx_data += sizeof(uint32_t);
+
+	switch (bitwidth) {
+	case 16:
+		fmt = STR_16BIT;
+		break;
+
+	case 24:
+		fmt = STR_24BIT;
+		break;
+
+	case 32:
+		fmt = STR_32BIT;
+		break;
+
+	default:
+		fmt = NULL;
+		dev_info(plat_data->dev,
+			"Not supported params bitwidth %d",
+					bitwidth);
+		break;
+	}
+
+	if (!fmt)
+		return -EINVAL;
+
+	for (i = 0; i < blk_count; i++) {
+		if (memcmp(fmt, rx_data, STR_RXBITS_SZ) == 0) {
+			rx_data += STR_RXBITS_SZ;
+			break;
+		} else {
+			rx_data += STR_RXBITS_SZ;
+			sublk_sz = *((uint32_t *)rx_data);
+			rx_data += sizeof(uint32_t);
+			rx_data += sublk_sz;
+		}
+	}
+
+	if (i < blk_count)
+		ret = tas25xx_process_block(p_tas25xx, rx_data, ch);
+	else
+		ret = -EINVAL;
+
+	if (ret == 0)
+		p_tas25xx->mn_rx_width = bitwidth;
+
+	return ret;
+}
+
+int tas_dev_interrupt_clear(struct tas25xx_priv *p_tas25xx, int chn)
+{
+	if (p_tas25xx->intr_data[chn].buf_intr_clear)
+		return tas25xx_process_block(p_tas25xx,
+			p_tas25xx->intr_data[chn].buf_intr_clear, chn);
+
+	return 0;
+}
+
+int tas_dev_interrupt_enable(struct tas25xx_priv *p_tas25xx, int chn)
+{
+	p_tas25xx->devs[chn]->irq_count = 0;
+
+	if (p_tas25xx->intr_data[chn].buf_intr_enable)
+		return tas25xx_process_block(p_tas25xx,
+			p_tas25xx->intr_data[chn].buf_intr_enable, chn);
+
+	return 0;
+}
+
+int tas_dev_interrupt_disable(struct tas25xx_priv *p_tas25xx, int chn)
+{
+	if (p_tas25xx->intr_data[chn].buf_intr_disable)
+		return tas25xx_process_block(p_tas25xx,
+			p_tas25xx->intr_data[chn].buf_intr_disable, chn);
+
+	return 0;
+}
+
+int tas_dev_interrupt_read(struct tas25xx_priv *p_tas25xx, int chn, int *type)
+{
+	int32_t ret = 0;
+	int32_t i;
+	int32_t reg = -1;
+	int32_t intr_detected = 0;
+	uint32_t value = 0;
+	int32_t powered_up = 0;
+
+	struct tas25xx_intr_info *intr_info;
+	struct linux_platform *plat_data =
+		(struct linux_platform *) p_tas25xx->platform_data;
+	struct tas25xx_interrupts *intr_data = &p_tas25xx->intr_data[chn];
+
+	powered_up = is_power_up_state(p_tas25xx->m_power_state);
+
+	for (i = 0; i < intr_data->count; i++) {
+		intr_info = &intr_data->intr_info[i];
+		if (!powered_up && intr_info->is_clock_based) {
+			/* ignore clock based interrupt during power off state */
+			dev_dbg(plat_data->dev,
+				"INTR: not checking for %s, reason: not active state",
+				intr_info->name);
+			continue;
+		}
+
+		if ((reg != intr_info->reg) || ret) {
+			reg = intr_info->reg;
+			ret = p_tas25xx->read(p_tas25xx, chn, reg, &value);
+			if (!ret)
+				dev_err(plat_data->dev,
+					"INTR: ch=%d reg=0x%2x(%d), value=0x%2x(%d)",
+					chn, reg, reg, value, value);
+		} else {
+			dev_dbg(plat_data->dev, "INTR: skipping reading reg = %d",
+				intr_info->reg);
+		}
+		if (ret) {
+			dev_err(plat_data->dev,
+				"INTR: Error reading the interrupt reg=%d, err=%d", reg, ret);
+		} else {
+			if (value & intr_info->mask) {
+				if (powered_up && intr_info->is_clock_based)
+					*type |= INTERRUPT_TYPE_CLOCK_BASED;
+				else
+					*type |= INTERRUPT_TYPE_NON_CLOCK_BASED;
+				dev_err(plat_data->dev, "INTR: Detected, ch=%d, intr=%s",
+					chn, intr_info->name);
+				intr_info->detected = 1;
+				if (intr_info->count < UINT32_MAX)
+					intr_info->count++;
+				if (intr_info->count_persist < UINT64_MAX)
+					intr_info->count_persist++;
+				intr_detected |= 1;
+			}
+		}
+	}
+
+	return intr_detected;
+}
+
+int tas25xx_set_power_mute(struct tas25xx_priv *p_tas25xx, int ch)
+{
+	return tas25xx_process_block(p_tas25xx,
+		p_tas25xx->block_op_data[ch].mute, ch);
+}
+
+int tas25xx_software_reset(struct tas25xx_priv *p_tas25xx, int ch)
+{
+	int ret;
+
+	ret = tas25xx_process_block(p_tas25xx,
+		p_tas25xx->block_op_data[ch].sw_reset, ch);
+
+	p_tas25xx->devs[ch]->mn_current_book = -1;
+
+	usleep_range(10000, 10100);
+
+	return ret;
+}

+ 27 - 0
qcom/opensource/audio-kernel/asoc/msm_common.c

@@ -403,6 +403,11 @@ int msm_common_snd_hw_params(struct snd_pcm_substream *substream,
 	struct msm_common_pdata *pdata = msm_common_get_pdata(card);
 	int index = get_mi2s_tdm_auxpcm_intf_index(stream_name);
 	struct clk_cfg intf_clk_cfg;
+#ifdef CONFIG_COMMON_AMP_CIRRUS
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+	unsigned int num_codecs = rtd->dai_link->num_codecs;
+	int i;
+#endif
 
 	dev_dbg(rtd->card->dev,
 		"%s: substream = %s  stream = %d\n",
@@ -447,6 +452,28 @@ int msm_common_snd_hw_params(struct snd_pcm_substream *substream,
 						__func__, ret);
 					goto done;
 				}
+#ifdef CONFIG_COMMON_AMP_CIRRUS
+				for (i = 0; i < num_codecs; i++) {
+					codec_dai = asoc_rtd_to_codec(rtd, i);
+					ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+							intf_clk_cfg.clk_freq_in_hz, SND_SOC_CLOCK_IN);
+					if (ret < 0)
+						pr_err("%s: failed to set codec tdm clk, err:%d\n",
+									__func__, ret);
+
+					ret = snd_soc_component_set_sysclk(codec_dai->component,
+							CLK_SRC_SCLK, 0, intf_clk_cfg.clk_freq_in_hz, SND_SOC_CLOCK_IN);
+					if (ret < 0)
+						pr_err("%s: failed to set component sys clk, err:%d\n",
+									__func__, ret);
+
+					ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, 0, 0, 32);
+					if (ret < 0)
+						pr_err("%s: failed to set tdm slot, err:%d\n",
+									__func__, ret);
+					
+				}
+#endif
 			} else if ((strnstr(stream_name, "MI2S", strlen(stream_name)))) {
 
 				ret =  get_mi2s_clk_id(index);

+ 19 - 0
qcom/opensource/audio-kernel/asoc/msm_common.h

@@ -9,6 +9,25 @@
 #include <sound/soc.h>
 #include <sound/pcm.h>
 
+#ifdef CONFIG_COMMON_AMP_CIRRUS
+#define CLK_SRC_SCLK 0
+#define CLK_SRC_LRCLK 1
+#define CLK_SRC_PDM 2
+#define CLK_SRC_SELF 3
+#define CLK_SRC_MCLK 4
+#define CLK_SRC_SWIRE 5
+#define CLK_SRC_DAI 0
+#define CLK_SRC_CODEC 1
+
+#define CS35L43_MONO   0x31
+#define CS35L43_STEREO 0x32
+#define CS35L43_QUAD    0x34
+
+#define CS35L45_MONO   0x51
+#define CS35L45_STEREO 0x52
+#define CS35L45_QUAD    0x54
+#endif
+
 enum {
 	MI2S = 0,
 	TDM,

+ 34 - 0
qcom/opensource/audio-kernel/asoc/msm_dailink.h

@@ -262,7 +262,11 @@ SND_SOC_DAILINK_DEFS(pri_mi2s_tx,
 
 SND_SOC_DAILINK_DEFS(sec_mi2s_rx,
 	DAILINK_COMP_ARRAY(COMP_CPU("snd-soc-dummy-dai")),
+#ifdef CONFIG_USE_CS40L26
+	DAILINK_COMP_ARRAY(COMP_CODEC("cs40l26-codec", "cs40l26-pcm")),
+#else
 	DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-rx")),
+#endif
 	DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy")));
 
 SND_SOC_DAILINK_DEFS(sec_mi2s_tx,
@@ -642,3 +646,33 @@ SND_SOC_DAILINK_DEFS(hs_if4_tdm_tx_0_dummy,
 	DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-tx")),
 	DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy")));
 
+#ifdef CONFIG_COMMON_AMP_CIRRUS
+SND_SOC_DAILINK_DEFS(cirrus_pri_tdm_rx_0,
+	DAILINK_COMP_ARRAY(COMP_CPU("snd-soc-dummy-dai")),
+	DAILINK_COMP_ARRAY(COMP_DUMMY(),
+			   COMP_DUMMY(),
+			   COMP_DUMMY(),
+			   COMP_DUMMY()),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy")));
+
+SND_SOC_DAILINK_DEFS(cirrus_pri_tdm_tx_0,
+	DAILINK_COMP_ARRAY(COMP_CPU("snd-soc-dummy-dai")),
+	DAILINK_COMP_ARRAY(COMP_DUMMY(),
+			   COMP_DUMMY(),
+			   COMP_DUMMY(),
+			   COMP_DUMMY()),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy")));
+#endif
+#ifdef CONFIG_SND_SOC_TAS25XX
+SND_SOC_DAILINK_DEFS(tas25xx_pri_tdm_rx_0,
+	DAILINK_COMP_ARRAY(COMP_CPU("snd-soc-dummy-dai")),
+	DAILINK_COMP_ARRAY(
+		COMP_CODEC("tas25xx.18-0048", "tas25xx ASI1")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy")));
+
+SND_SOC_DAILINK_DEFS(tas25xx_pri_tdm_tx_0,
+	DAILINK_COMP_ARRAY(COMP_CPU("snd-soc-dummy-dai")),
+	DAILINK_COMP_ARRAY(
+		COMP_CODEC("tas25xx.18-0048", "tas25xx ASI1")),
+	DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy")));
+#endif

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 922 - 2
qcom/opensource/audio-kernel/asoc/pineapple.c


+ 3 - 0
qcom/opensource/audio-kernel/audio_kernel_modules.mk

@@ -75,6 +75,9 @@ ifeq ($(call is-board-platform-in-list, holi blair), true)
 AUDIO_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/wcd938x_dlkm.ko \
 	$(KERNEL_MODULES_OUT)/wcd938x_slave_dlkm.ko
 endif
+ifeq ($(PROJECT_NAME),$(filter $(PROJECT_NAME),q6q b6q q6aq))
+AUDIO_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/tas25xx_dlkm.ko
+endif
 endif
 else
 ifeq ($(call is-board-platform-in-list, gen4 msmnile), true)

+ 3 - 1
qcom/opensource/audio-kernel/audio_kernel_product_board.mk

@@ -60,7 +60,9 @@ ifeq ($(call is-board-platform-in-list, holi blair), true)
 PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/wcd938x_dlkm.ko \
 	$(KERNEL_MODULES_OUT)/wcd938x_slave_dlkm.ko
 endif
-
+ifeq ($(PROJECT_NAME),$(filter $(PROJECT_NAME),q6q b6q q6aq))
+PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/tas25xx_dlkm.ko
+endif
 ifeq ($(call is-board-platform-in-list, gen4 msmnile), true)
 ifneq (,$(filter $(TARGET_BOARD_PLATFORM)$(TARGET_BOARD_SUFFIX), gen4_gvm msmnile_gvmq))
 PRODUCT_PACKAGES  += $(KERNEL_MODULES_OUT)/machine_dlkm.ko \

+ 54 - 1
qcom/opensource/audio-kernel/dsp/adsp-loader.c

@@ -19,12 +19,17 @@
 #include <linux/slab.h>
 #include <linux/remoteproc.h>
 #include <linux/remoteproc/qcom_rproc.h>
-
+#if IS_ENABLED(CONFIG_SND_SOC_SAMSUNG_AUDIO)
+#include <sound/samsung/snd_debug_proc.h>
+#endif
 
 #define Q6_PIL_GET_DELAY_MS 100
 #define BOOT_CMD 1
 #define SSR_RESET_CMD 1
 #define IMAGE_UNLOAD_CMD 0
+#if IS_ENABLED(CONFIG_SEC_SENSORS_SSC)
+#define SUB_SNS_VDD_CHECK_CMD 0
+#endif
 #define MAX_FW_IMAGES 4
 #define ADSP_LOADER_APM_TIMEOUT_MS 10000
 
@@ -43,6 +48,12 @@ static ssize_t adsp_ssr_store(struct kobject *kobj,
 	struct kobj_attribute *attr,
 	const char *buf, size_t count);
 
+#if IS_ENABLED(CONFIG_SEC_SENSORS_SSC)
+static ssize_t adsp_check_store(struct kobject *kobj,
+	struct kobj_attribute *attr,
+	const char *buf, size_t count);
+#endif
+
 struct adsp_loader_private {
 	void *pil_h;
 	struct kobject *boot_adsp_obj;
@@ -57,9 +68,17 @@ static struct kobj_attribute adsp_boot_attribute =
 static struct kobj_attribute adsp_ssr_attribute =
 	__ATTR(ssr, 0220, NULL, adsp_ssr_store);
 
+#if IS_ENABLED(CONFIG_SEC_SENSORS_SSC)
+static struct kobj_attribute adsp_check_attribute =
+	__ATTR(check, 0220, NULL, adsp_check_store);
+#endif
+
 static struct attribute *attrs[] = {
 	&adsp_boot_attribute.attr,
 	&adsp_ssr_attribute.attr,
+#if IS_ENABLED(CONFIG_SEC_SENSORS_SSC)
+	&adsp_check_attribute.attr,
+#endif
 	NULL,
 };
 
@@ -172,6 +191,10 @@ load_adsp:
 			if (rc) {
 				dev_err(&pdev->dev, "%s: pil get failed,\n",
 					__func__);
+#if IS_ENABLED(CONFIG_SND_SOC_SAMSUNG_AUDIO)
+				sdp_boot_print("%s: ADSP loadig is failed = %d\n",
+					__func__, rc);
+#endif
 				goto fail;
 			}
 		} else if (adsp_state == SPF_SUBSYS_LOADED) {
@@ -249,6 +272,36 @@ static ssize_t adsp_boot_store(struct kobject *kobj,
 	return count;
 }
 
+#if IS_ENABLED(CONFIG_SEC_SENSORS_SSC)
+static ssize_t adsp_check_store(struct kobject *kobj,
+	struct kobj_attribute *attr,
+	const char *buf,
+	size_t count)
+{
+	int check_command = 0;
+
+	if (kstrtoint(buf, 10, &check_command) < 0)
+		return -EINVAL;
+
+	if (check_command == SUB_SNS_VDD_CHECK_CMD) {
+		struct platform_device *pdev = adsp_private;
+		struct adsp_loader_private *priv = NULL;
+		struct rproc *adsp_rproc = NULL;
+
+		priv = platform_get_drvdata(pdev);
+		if (priv) {
+			adsp_rproc = (struct rproc *)priv->pil_h;
+			if (adsp_rproc) {
+				pr_info("check subsensor vdd\n");
+				adsp_init_subsensor_regulator(adsp_rproc,
+					NULL);
+			}
+		}
+	}
+	return count;
+}
+#endif
+
 static void adsp_loader_unload(struct platform_device *pdev)
 {
 	struct adsp_loader_private *priv = NULL;

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott