Audio porting for smart amp TAS2557.

Root cause:
Audio porting for smart amp TAS2557.
How to fix:
N/A

Change-Id: I9b9ea4f13ac2432fd3e2104438ff7362406a1428
RiskArea:speaker
(cherry picked from commit 55d13014376b9950ada458d4e16b26a6a28f67f9)
(cherry picked from commit bde46d6932cf950e4e671647c57e5628fd9b890e)
diff --git a/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
index 10c7195..7b4d8b2 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
@@ -72,6 +72,17 @@
 	        status = "okay";
 	    };
 	    /* AWINIC AW8898 Smart PA End */
+
+	    tas2557@4c {
+		sound-dai-cells = <1>;
+		compatible = "ti,tas2557";
+		reg = <0x4c>;
+		ti,cdc-reset-gpio = <&tlmm 21 0>;
+		ti,irq-gpio = <&tlmm 20 0>;
+		ti,i2s-bits = <16>;
+		ti,bypass-tmax = <0>;
+		status = "ok";
+	    };
 };
 //[FairPhone][Audio][jinjia]=2018.11.21=smart amp porting. -e
 
diff --git a/arch/arm64/configs/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index b416dcd..f444ba2 100755
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -696,3 +696,9 @@
 #add aw8898 smartpa
 CONFIG_SND_SMARTPA_AW8898=y
 #[FairPhone][Audio][jinjia]=2018.11.21=smart amp porting. -e
+#[FairPhone][Audio][jinjia]=2020.05.19=2nd smart amp porting. -s
+CONFIG_SND_SOC_TAS2557=y
+CONFIG_TAS2557_REGMAP=y
+CONFIG_TAS2557_CODEC=y
+CONFIG_TAS2557_MISC=y
+#[FairPhone][Audio][jinjia]=2020.05.19=2nd smart amp porting. -e
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index 4ea5401..71df90c 100755
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -760,3 +760,9 @@
 #add aw8898 smartpa
 CONFIG_SND_SMARTPA_AW8898=y
 #[FairPhone][Audio][jinjia]=2018.11.21=smart amp porting. -e
+#[FairPhone][Audio][jinjia]=2020.05.19=2nd smart amp porting. -s
+CONFIG_SND_SOC_TAS2557=y
+CONFIG_TAS2557_REGMAP=y
+CONFIG_TAS2557_CODEC=y
+CONFIG_TAS2557_MISC=y
+#[FairPhone][Audio][jinjia]=2020.05.19=2nd smart amp porting. -e
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 9eab034..6613ff4 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1105,6 +1105,8 @@
 		This option enables support for aw8898 series Smart PA.
 #[FairPhone][Audio][jinjia]=2018.11.21=smart amp porting. -e
 
+source "sound/soc/codecs/tas2557/Kconfig"
+
 config SND_SOC_TFA98XX
 	tristate "NXP Semiconductors TFA98XX amplifier"
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 459e5fd..3352c8c 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -451,3 +451,4 @@
 #for AWINIC AW8898 Smart PA
 obj-y += aw/aw8898.o
 #[FairPhone][Audio][jinjia]=2018.11.21=smart amp porting. -e
+obj-$(CONFIG_SND_SOC_TAS2557) += tas2557/
diff --git a/sound/soc/codecs/tas2557/Kconfig b/sound/soc/codecs/tas2557/Kconfig
new file mode 100644
index 0000000..74ba53d
--- /dev/null
+++ b/sound/soc/codecs/tas2557/Kconfig
@@ -0,0 +1,15 @@
+
+menuconfig SND_SOC_TAS2557
+	tristate "Texas Instruments TAS2557 SmartAmp(R)"
+
+if SND_SOC_TAS2557
+config TAS2557_REGMAP
+    bool "Use of RegMap API"
+
+config TAS2557_CODEC
+    bool "Codec Driver support"
+
+config TAS2557_MISC
+    bool "Misc Driver support"
+
+endif # SND_SOC_TAS2557
diff --git a/sound/soc/codecs/tas2557/Makefile b/sound/soc/codecs/tas2557/Makefile
new file mode 100644
index 0000000..1287372
--- /dev/null
+++ b/sound/soc/codecs/tas2557/Makefile
@@ -0,0 +1,2 @@
+snd-soc-tas2557-objs := tas2557-core.o tas2557-regmap.o tas2557-codec.o tas2557-misc.o tiload.o
+obj-$(CONFIG_SND_SOC_TAS2557) += snd-soc-tas2557.o
diff --git a/sound/soc/codecs/tas2557/tas2557-codec.c b/sound/soc/codecs/tas2557/tas2557-codec.c
new file mode 100644
index 0000000..af07285
--- /dev/null
+++ b/sound/soc/codecs/tas2557/tas2557-codec.c
@@ -0,0 +1,639 @@
+/*
+** =============================================================================
+** 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:
+**     tas2557-codec.c
+**
+** Description:
+**     ALSA SoC driver for Texas Instruments TAS2557 High Performance 4W Smart Amplifier
+**
+** =============================================================================
+*/
+
+#ifdef CONFIG_TAS2557_CODEC
+
+#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/uaccess.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 "tas2557-core.h"
+#include "tas2557-codec.h"
+
+#define KCONTROL_CODEC
+
+static unsigned int tas2557_codec_read(struct snd_soc_codec *pCodec,
+	unsigned int nRegister)
+{
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+	int ret = 0;
+	unsigned int Value = 0;
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	ret = pTAS2557->read(pTAS2557, nRegister, &Value);
+	if (ret < 0)
+		dev_err(pTAS2557->dev, "%s, %d, ERROR happen=%d\n", __func__,
+			__LINE__, ret);
+	else
+		ret = Value;
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return ret;
+}
+
+static int tas2557_codec_write(struct snd_soc_codec *pCodec, unsigned int nRegister,
+	unsigned int nValue)
+{
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+	int ret = 0;
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	ret = pTAS2557->write(pTAS2557, nRegister, nValue);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return ret;
+}
+
+static int tas2557_codec_suspend(struct snd_soc_codec *pCodec)
+{
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+	int ret = 0;
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	dev_dbg(pTAS2557->dev, "%s\n", __func__);
+	pTAS2557->runtime_suspend(pTAS2557);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return ret;
+}
+
+static int tas2557_codec_resume(struct snd_soc_codec *pCodec)
+{
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+	int ret = 0;
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	dev_dbg(pTAS2557->dev, "%s\n", __func__);
+	pTAS2557->runtime_resume(pTAS2557);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return ret;
+}
+
+static const struct snd_soc_dapm_widget tas2557_dapm_widgets[] = {
+	SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("ASI2", "ASI2 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("ASIM", "ASIM Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_OUT_DRV("ClassD", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_SUPPLY("PLL", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("NDivider", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_OUTPUT("OUT")
+};
+
+static const struct snd_soc_dapm_route tas2557_audio_map[] = {
+	{"DAC", NULL, "ASI1"},
+	{"DAC", NULL, "ASI2"},
+	{"DAC", NULL, "ASIM"},
+	{"ClassD", NULL, "DAC"},
+	{"OUT", NULL, "ClassD"},
+	{"DAC", NULL, "PLL"},
+	{"DAC", NULL, "NDivider"},
+};
+
+static int tas2557_startup(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+	printk("[tas2557] %s\n", __func__); //[jinjia]Trace
+
+	dev_dbg(pTAS2557->dev, "%s\n", __func__);
+	return 0;
+}
+
+static void tas2557_shutdown(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+	printk("[tas2557] %s\n", __func__); //[jinjia]Trace
+	dev_dbg(pTAS2557->dev, "%s\n", __func__);
+}
+
+static int tas2557_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+	printk("[tas2557] %s\n", __func__); //[jinjia]Trace
+	mutex_lock(&pTAS2557->codec_lock);
+
+	dev_dbg(pTAS2557->dev, "%s\n", __func__);
+	tas2557_enable(pTAS2557, !mute);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return 0;
+}
+
+static int tas2557_set_dai_sysclk(struct snd_soc_dai *pDAI,
+	int nClkID, unsigned int nFreqency, int nDir)
+{
+	struct snd_soc_codec *pCodec = pDAI->codec;
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+
+	printk("[tas2557] %s\n", __func__); //[jinjia]Trace
+	dev_dbg(pTAS2557->dev, "tas2557_set_dai_sysclk: freq = %u\n", nFreqency);
+
+	return 0;
+}
+
+static int tas2557_hw_params(struct snd_pcm_substream *pSubstream,
+	struct snd_pcm_hw_params *pParams, struct snd_soc_dai *pDAI)
+{
+	struct snd_soc_codec *pCodec = pDAI->codec;
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+
+	printk("[tas2557] %s\n", __func__); //[jinjia]Trace
+	mutex_lock(&pTAS2557->codec_lock);
+
+	dev_dbg(pTAS2557->dev, "%s\n", __func__);
+/* do bit rate setting during platform data */
+/* tas2557_set_bit_rate(pTAS2557, channel_both, snd_pcm_format_width(params_format(pParams))); */
+	tas2557_set_sampling_rate(pTAS2557, params_rate(pParams));
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return 0;
+}
+
+static int tas2557_set_dai_fmt(struct snd_soc_dai *pDAI, unsigned int nFormat)
+{
+	struct snd_soc_codec *codec = pDAI->codec;
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(pTAS2557->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int tas2557_prepare(struct snd_pcm_substream *pSubstream,
+	struct snd_soc_dai *pDAI)
+{
+	struct snd_soc_codec *codec = pDAI->codec;
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(pTAS2557->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int tas2557_set_bias_level(struct snd_soc_codec *pCodec,
+	enum snd_soc_bias_level eLevel)
+{
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+
+	dev_dbg(pTAS2557->dev, "%s: %d\n", __func__, eLevel);
+	return 0;
+}
+
+static int tas2557_codec_probe(struct snd_soc_codec *pCodec)
+{
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(pCodec);
+
+	dev_dbg(pTAS2557->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int tas2557_codec_remove(struct snd_soc_codec *pCodec)
+{
+	return 0;
+}
+
+static int tas2557_power_ctrl_get(struct snd_kcontrol *pKcontrol,
+	struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+	struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	pValue->value.integer.value[0] = pTAS2557->mbPowerUp;
+	dev_dbg(pTAS2557->dev, "tas2557_power_ctrl_get = %d\n",
+		pTAS2557->mbPowerUp);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return 0;
+}
+
+static int tas2557_power_ctrl_put(struct snd_kcontrol *pKcontrol,
+	struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+	struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+	int nPowerOn = pValue->value.integer.value[0];
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	dev_dbg(pTAS2557->dev, "tas2557_power_ctrl_put = %d\n", nPowerOn);
+	tas2557_enable(pTAS2557, (nPowerOn != 0));
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return 0;
+}
+
+static int tas2557_fs_get(struct snd_kcontrol *pKcontrol,
+	struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+	struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+	int nFS = 48000;
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	if (pTAS2557->mpFirmware->mnConfigurations)
+		nFS = pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration].mnSamplingRate;
+	pValue->value.integer.value[0] = nFS;
+	dev_dbg(pTAS2557->dev, "tas2557_fs_get = %d\n", nFS);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return 0;
+}
+
+static int tas2557_fs_put(struct snd_kcontrol *pKcontrol,
+	struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+	struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+	int nFS = pValue->value.integer.value[0];
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	dev_info(pTAS2557->dev, "tas2557_fs_put = %d\n", nFS);
+	ret = tas2557_set_sampling_rate(pTAS2557, nFS);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return ret;
+}
+
+static int tas2557_Cali_get(struct snd_kcontrol *pKcontrol,
+	struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+	struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+	bool ret = 0;
+	int prm_r0 = 0;
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	ret = tas2557_get_Cali_prm_r0(pTAS2557, &prm_r0);
+	if (ret)
+		pValue->value.integer.value[0] = prm_r0;
+
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return 0;
+}
+
+static int tas2557_program_get(struct snd_kcontrol *pKcontrol,
+	struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+	struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	pValue->value.integer.value[0] = pTAS2557->mnCurrentProgram;
+	dev_dbg(pTAS2557->dev, "tas2557_program_get = %d\n",
+		pTAS2557->mnCurrentProgram);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return 0;
+}
+
+static int tas2557_program_put(struct snd_kcontrol *pKcontrol,
+	struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+	struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+	unsigned int nProgram = pValue->value.integer.value[0];
+	int ret = 0, nConfiguration = -1;
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	if (nProgram == pTAS2557->mnCurrentProgram)
+		nConfiguration = pTAS2557->mnCurrentConfiguration;
+	ret = tas2557_set_program(pTAS2557, nProgram, nConfiguration);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return ret;
+}
+
+static int tas2557_configuration_get(struct snd_kcontrol *pKcontrol,
+	struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+	struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	pValue->value.integer.value[0] = pTAS2557->mnCurrentConfiguration;
+	dev_dbg(pTAS2557->dev, "tas2557_configuration_get = %d\n",
+		pTAS2557->mnCurrentConfiguration);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return 0;
+}
+
+static int tas2557_configuration_put(struct snd_kcontrol *pKcontrol,
+	struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+	struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+	unsigned int nConfiguration = pValue->value.integer.value[0];
+	int ret = 0;
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	dev_info(pTAS2557->dev, "%s = %d\n", __func__, nConfiguration);
+	ret = tas2557_set_config(pTAS2557, nConfiguration);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return ret;
+}
+
+static int tas2557_calibration_get(struct snd_kcontrol *pKcontrol,
+	struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+	struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	pValue->value.integer.value[0] = pTAS2557->mnCurrentCalibration;
+	dev_info(pTAS2557->dev,
+		"tas2557_calibration_get = %d\n",
+		pTAS2557->mnCurrentCalibration);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return 0;
+}
+
+static int tas2557_calibration_put(struct snd_kcontrol *pKcontrol,
+	struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+	struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+	unsigned int nCalibration = pValue->value.integer.value[0];
+	int ret = 0;
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	ret = tas2557_set_calibration(pTAS2557, nCalibration);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return ret;
+}
+
+static const char * const classd_edge_text[] = {
+	"0 (50ns)",
+	"1 (40ns)",
+	"2 (29ns)",
+	"3 (25ns)",
+	"4 (14ns)",
+	"5 (13ns)",
+	"6 (12ns)",
+	"7 (11ns)",
+};
+
+static const struct soc_enum classd_edge_enum[] = {
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(classd_edge_text), classd_edge_text),
+};
+
+static int tas2557_edge_get(struct snd_kcontrol *pKcontrol,
+			struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+	struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+	mutex_lock(&pTAS2557->codec_lock);
+
+	pValue->value.integer.value[0] = pTAS2557->mnEdge;
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return 0;
+}
+static int tas2557_edge_put(struct snd_kcontrol *pKcontrol,
+			struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+	struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+	struct tas2557_priv *pTAS2557 = snd_soc_codec_get_drvdata(codec);
+	unsigned int edge = pValue->value.integer.value[0];
+
+	mutex_lock(&pTAS2557->codec_lock);
+
+	dev_dbg(pTAS2557->dev, "%s, edge %d\n", __func__, edge);
+	pTAS2557->mnEdge = pValue->value.integer.value[0];
+	tas2557_update_edge(pTAS2557);
+
+	mutex_unlock(&pTAS2557->codec_lock);
+	return 0;
+}
+
+static const struct snd_kcontrol_new tas2557_snd_controls[] = {
+	SOC_SINGLE_EXT("PowerCtrl", SND_SOC_NOPM, 0, 0x0001, 0,
+		tas2557_power_ctrl_get, tas2557_power_ctrl_put),
+	SOC_SINGLE_EXT("Program", SND_SOC_NOPM, 0, 0x00FF, 0, tas2557_program_get,
+		tas2557_program_put),
+	SOC_SINGLE_EXT("Configuration", SND_SOC_NOPM, 0, 0x00FF, 0,
+		tas2557_configuration_get, tas2557_configuration_put),
+	SOC_SINGLE_EXT("FS", SND_SOC_NOPM, 8000, 48000, 0,
+		tas2557_fs_get, tas2557_fs_put),
+	SOC_SINGLE_EXT("Get Cali_Re", SND_SOC_NOPM, 0, 0x7f000000, 0,
+		tas2557_Cali_get, NULL),
+	SOC_SINGLE_EXT("Calibration", SND_SOC_NOPM, 0, 0x00FF, 0,
+		tas2557_calibration_get, tas2557_calibration_put),
+	SOC_ENUM_EXT("TAS2557 ClassD Edge", classd_edge_enum[0],
+		tas2557_edge_get, tas2557_edge_put),
+};
+
+static struct snd_soc_codec_driver soc_codec_driver_tas2557 = {
+	.probe = tas2557_codec_probe,
+	.remove = tas2557_codec_remove,
+	.read = tas2557_codec_read,
+	.write = tas2557_codec_write,
+	.suspend = tas2557_codec_suspend,
+	.resume = tas2557_codec_resume,
+	.set_bias_level = tas2557_set_bias_level,
+	.idle_bias_off = true,
+	.component_driver = {
+		.controls = tas2557_snd_controls,
+		.num_controls = ARRAY_SIZE(tas2557_snd_controls),
+		.dapm_widgets = tas2557_dapm_widgets,
+		.num_dapm_widgets = ARRAY_SIZE(tas2557_dapm_widgets),
+		.dapm_routes = tas2557_audio_map,
+		.num_dapm_routes = ARRAY_SIZE(tas2557_audio_map),
+	},
+};
+
+static struct snd_soc_dai_ops tas2557_dai_ops = {
+	.startup = tas2557_startup,
+	.shutdown = tas2557_shutdown,
+	.digital_mute = tas2557_mute,
+	.hw_params = tas2557_hw_params,
+	.prepare = tas2557_prepare,
+	.set_sysclk = tas2557_set_dai_sysclk,
+	.set_fmt = tas2557_set_dai_fmt,
+};
+
+#define TAS2557_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 tas2557_dai_driver[] = {
+	{
+		.name = "tas2557 ASI1",
+		.id = 0,
+		.playback = {
+				.stream_name = "ASI1 Playback",
+				.channels_min = 2,
+				.channels_max = 2,
+				.rates = SNDRV_PCM_RATE_8000_192000,
+				.formats = TAS2557_FORMATS,
+			},
+		.ops = &tas2557_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "tas2557 ASI2",
+		.id = 1,
+		.playback = {
+				.stream_name = "ASI2 Playback",
+				.channels_min = 2,
+				.channels_max = 2,
+				.rates = SNDRV_PCM_RATE_8000_192000,
+				.formats = TAS2557_FORMATS,
+			},
+		.ops = &tas2557_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "tas2557 ASIM",
+		.id = 2,
+		.playback = {
+				.stream_name = "ASIM Playback",
+				.channels_min = 2,
+				.channels_max = 2,
+				.rates = SNDRV_PCM_RATE_8000_192000,
+				.formats = TAS2557_FORMATS,
+			},
+		.ops = &tas2557_dai_ops,
+		.symmetric_rates = 1,
+	},
+};
+
+int tas2557_register_codec(struct tas2557_priv *pTAS2557)
+{
+	int nResult = 0;
+
+	dev_info(pTAS2557->dev, "%s, enter\n", __func__);
+	nResult = snd_soc_register_codec(pTAS2557->dev,
+		&soc_codec_driver_tas2557,
+		tas2557_dai_driver, ARRAY_SIZE(tas2557_dai_driver));
+	return nResult;
+}
+
+int tas2557_deregister_codec(struct tas2557_priv *pTAS2557)
+{
+	snd_soc_unregister_codec(pTAS2557->dev);
+	return 0;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2557 ALSA SOC Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
+#endif
diff --git a/sound/soc/codecs/tas2557/tas2557-codec.h b/sound/soc/codecs/tas2557/tas2557-codec.h
new file mode 100644
index 0000000..8e20270
--- /dev/null
+++ b/sound/soc/codecs/tas2557/tas2557-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:
+**     tas2557-codec.h
+**
+** Description:
+**     header file for tas2557-codec.c
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2557_CODEC_H
+#define _TAS2557_CODEC_H
+
+#include "tas2557.h"
+
+int tas2557_register_codec(struct tas2557_priv *pTAS2557);
+int tas2557_deregister_codec(struct tas2557_priv *pTAS2557);
+
+#endif /* _TAS2557_CODEC_H */
diff --git a/sound/soc/codecs/tas2557/tas2557-core.c b/sound/soc/codecs/tas2557/tas2557-core.c
new file mode 100644
index 0000000..9109f8d
--- /dev/null
+++ b/sound/soc/codecs/tas2557/tas2557-core.c
@@ -0,0 +1,2120 @@
+/*
+** =============================================================================
+** 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:
+**     tas2557-core.c
+**
+** Description:
+**     TAS2557 common functions for Android Linux
+**
+** =============================================================================
+*/
+
+#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/uaccess.h>
+#include <linux/crc8.h>
+
+#include "tas2557.h"
+#include "tas2557-core.h"
+
+#define	PPC_DRIVER_CRCCHK			0x00000200
+#define	PPC_DRIVER_CONFDEV			0x00000300
+#define	PPC_DRIVER_MTPLLSRC			0x00000400
+#define	PPC_DRIVER_CFGDEV_NONCRC	0x00000101
+
+#define TAS2557_CAL_NAME    "/data/tas2557_cal.bin"
+#define RESTART_MAX 3
+
+static int tas2557_load_calibration(struct tas2557_priv *pTAS2557,
+	char *pFileName);
+static int tas2557_load_data(struct tas2557_priv *pTAS2557, struct TData *pData,
+	unsigned int nType);
+static void tas2557_clear_firmware(struct TFirmware *pFirmware);
+static int tas2557_load_block(struct tas2557_priv *pTAS2557, struct TBlock *pBlock);
+static int tas2557_load_configuration(struct tas2557_priv *pTAS2557,
+	unsigned int nConfiguration, bool bLoadSame);
+
+#define TAS2557_UDELAY 0xFFFFFFFE
+#define TAS2557_MDELAY 0xFFFFFFFD
+
+#define TAS2557_BLOCK_PLL				0x00
+#define TAS2557_BLOCK_PGM_ALL			0x0d
+#define TAS2557_BLOCK_PGM_DEV_A			0x01
+#define TAS2557_BLOCK_PGM_DEV_B			0x08
+#define TAS2557_BLOCK_CFG_COEFF_DEV_A	0x03
+#define TAS2557_BLOCK_CFG_COEFF_DEV_B	0x0a
+#define TAS2557_BLOCK_CFG_PRE_DEV_A		0x04
+#define TAS2557_BLOCK_CFG_PRE_DEV_B		0x0b
+#define TAS2557_BLOCK_CFG_POST			0x05
+#define TAS2557_BLOCK_CFG_POST_POWER	0x06
+
+static unsigned int p_tas2557_default_data[] = {
+	TAS2557_SAR_ADC2_REG, 0x05,	/* enable SAR ADC */
+	TAS2557_CLK_ERR_CTRL2, 0x21,	/*clk1:clock hysteresis, 0.34ms; clock halt, 22ms*/
+	TAS2557_CLK_ERR_CTRL3, 0x21,	/*clk2: rampDown 15dB/us, clock hysteresis, 10.66us; clock halt, 22ms */
+	TAS2557_SAFE_GUARD_REG, TAS2557_SAFE_GUARD_PATTERN,	/* safe guard */
+	0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2557_irq_config[] = {
+	TAS2557_CLK_HALT_REG, 0x71,	/* enable clk halt detect2 interrupt */
+	TAS2557_INT_GEN1_REG, 0x11,	/* enable spk OC and OV */
+	TAS2557_INT_GEN2_REG, 0x11,	/* enable clk err1 and die OT */
+	TAS2557_INT_GEN3_REG, 0x11,	/* enable clk err2 and brownout */
+	TAS2557_INT_GEN4_REG, 0x01,	/* disable SAR, enable clk halt */
+	TAS2557_GPIO4_PIN_REG, 0x07,	/* set GPIO4 as int1, default */
+	TAS2557_INT_MODE_REG, 0x80,	/* active high until INT_STICKY_1 and INT_STICKY_2 are read to be cleared. */
+	0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2557_startup_data[] = {
+	TAS2557_GPI_PIN_REG, 0x15,	/* enable DIN, MCLK, CCI */
+	TAS2557_GPIO1_PIN_REG, 0x01,	/* enable BCLK */
+	TAS2557_GPIO2_PIN_REG, 0x01,	/* enable WCLK */
+	TAS2557_POWER_CTRL2_REG, 0xA0,	 /* Class-D, Boost power up */
+	TAS2557_POWER_CTRL2_REG, 0xA3,	 /* Class-D, Boost, IV sense power up */
+	TAS2557_POWER_CTRL1_REG, 0xF8,	 /* PLL, DSP, clock dividers power up */
+	TAS2557_UDELAY, 2000,		/* delay */
+	TAS2557_CLK_ERR_CTRL, 0x2b,	/* enable clock error detection */
+	0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2557_unmute_data[] = {
+	TAS2557_MUTE_REG, 0x00,		 /* unmute */
+	TAS2557_SOFT_MUTE_REG, 0x00,	 /* soft unmute */
+	0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static unsigned int p_tas2557_shutdown_data[] = {
+	TAS2557_CLK_ERR_CTRL, 0x00,	 /* disable clock error detection */
+	TAS2557_SOFT_MUTE_REG, 0x01,	 /* soft mute */
+	TAS2557_UDELAY, 10000,		 /* delay 10ms */
+	TAS2557_MUTE_REG, 0x03,		 /* mute */
+	TAS2557_POWER_CTRL1_REG, 0x60,	 /* DSP power down */
+	TAS2557_UDELAY, 2000,		 /* delay 2ms */
+	TAS2557_POWER_CTRL2_REG, 0x00,	 /* Class-D, Boost power down */
+	TAS2557_POWER_CTRL1_REG, 0x00,	 /* all power down */
+	TAS2557_GPIO1_PIN_REG, 0x00,	/* disable BCLK */
+	TAS2557_GPIO2_PIN_REG, 0x00,	/* disable WCLK */
+	TAS2557_GPI_PIN_REG, 0x00,	/* disable DIN, MCLK, CCI */
+	0xFFFFFFFF, 0xFFFFFFFF
+};
+
+static int tas2557_dev_load_data(struct tas2557_priv *pTAS2557,
+	unsigned int *pData)
+{
+	int ret = 0;
+	unsigned int n = 0;
+	unsigned int nRegister;
+	unsigned int nData;
+
+	do {
+		nRegister = pData[n * 2];
+		nData = pData[n * 2 + 1];
+		if (nRegister == TAS2557_UDELAY)
+			udelay(nData);
+		else if (nRegister != 0xFFFFFFFF) {
+			ret = pTAS2557->write(pTAS2557, nRegister, nData);
+			if (ret < 0)
+				break;
+		}
+		n++;
+	} while (nRegister != 0xFFFFFFFF);
+	return ret;
+}
+
+int tas2557_configIRQ(struct tas2557_priv *pTAS2557)
+{
+	return tas2557_dev_load_data(pTAS2557, p_tas2557_irq_config);
+}
+
+int tas2557_set_bit_rate(struct tas2557_priv *pTAS2557, unsigned int nBitRate)
+{
+	int ret = 0, n = -1;
+
+	dev_dbg(pTAS2557->dev, "tas2557_set_bit_rate: nBitRate = %d\n", nBitRate);
+
+	switch (nBitRate) {
+	case 16:
+		n = 0;
+		break;
+	case 20:
+		n = 1;
+		break;
+	case 24:
+		n = 2;
+		break;
+	case 32:
+		n = 3;
+		break;
+	}
+
+	if (n >= 0)
+		ret = pTAS2557->update_bits(pTAS2557, TAS2557_ASI1_DAC_FORMAT_REG, 0x18, n<<3);
+	return ret;
+}
+
+int tas2557_get_bit_rate(struct tas2557_priv *pTAS2557, unsigned char *pBitRate)
+{
+	int ret = 0;
+	unsigned int nValue = 0;
+	unsigned char bitRate;
+
+	ret = pTAS2557->read(pTAS2557, TAS2557_ASI1_DAC_FORMAT_REG, &nValue);
+	if (ret >= 0) {
+		bitRate = (nValue&0x18)>>3;
+		if (bitRate == 0)
+			bitRate = 16;
+		else if (bitRate == 1)
+			bitRate = 20;
+		else if (bitRate == 2)
+			bitRate = 24;
+		else if (bitRate == 3)
+			bitRate = 32;
+		*pBitRate = bitRate;
+	}
+
+	return ret;
+}
+
+int tas2557_get_DAC_gain(struct tas2557_priv *pTAS2557, unsigned char *pnGain)
+{
+	int ret = 0;
+	unsigned int nValue = 0;
+
+	ret = pTAS2557->read(pTAS2557, TAS2557_SPK_CTRL_REG, &nValue);
+	if (ret >= 0)
+		*pnGain = ((nValue&TAS2557_DAC_GAIN_MASK)>>TAS2557_DAC_GAIN_SHIFT);
+
+	return ret;
+}
+
+int tas2557_set_DAC_gain(struct tas2557_priv *pTAS2557, unsigned int nGain)
+{
+	int ret = 0;
+
+	ret = pTAS2557->update_bits(pTAS2557, TAS2557_SPK_CTRL_REG, TAS2557_DAC_GAIN_MASK,
+		(nGain<<TAS2557_DAC_GAIN_SHIFT));
+	return ret;
+}
+
+/*
+* die temperature calculation:
+* DieTemp = readout / 2^23
+*/
+int tas2557_get_die_temperature(struct tas2557_priv *pTAS2557, int *pTemperature)
+{
+	int nResult = 0;
+	unsigned char nBuf[4];
+	int temp;
+
+	if (!pTAS2557->mpFirmware->mnConfigurations) {
+		dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__);
+		goto end;
+	}
+
+	if (!pTAS2557->mbPowerUp) {
+		dev_err(pTAS2557->dev, "%s, device not powered on\n", __func__);
+		goto end;
+	}
+
+	nResult = pTAS2557->bulk_read(pTAS2557, TAS2557_DIE_TEMP_REG, nBuf, 4);
+	if (nResult >= 0) {
+		temp = ((int)nBuf[0] << 24) | ((int)nBuf[1] << 16) | ((int)nBuf[2] << 8) | nBuf[3];
+		*pTemperature = temp;
+	}
+
+end:
+
+	return nResult;
+}
+
+int tas2557_load_platdata(struct tas2557_priv *pTAS2557)
+{
+	int nResult = 0;
+
+	if (gpio_is_valid(pTAS2557->mnGpioINT)) {
+		nResult = tas2557_configIRQ(pTAS2557);
+		if (nResult < 0)
+			goto end;
+	}
+
+	nResult = tas2557_set_bit_rate(pTAS2557, pTAS2557->mnI2SBits);
+
+end:
+
+	return nResult;
+}
+
+int tas2557_load_default(struct tas2557_priv *pTAS2557)
+{
+	int nResult = 0;
+
+	nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_default_data);
+	if (nResult < 0)
+		goto end;
+
+	nResult = tas2557_load_platdata(pTAS2557);
+	if (nResult < 0)
+		goto end;
+
+	/* enable DOUT tri-state for extra BCLKs */
+	nResult = pTAS2557->update_bits(pTAS2557, TAS2557_ASI1_DAC_FORMAT_REG, 0x01, 0x01);
+end:
+
+	return nResult;
+}
+
+static void failsafe(struct tas2557_priv *pTAS2557)
+{
+	int ret;
+
+	dev_err(pTAS2557->dev, "%s\n", __func__);
+	pTAS2557->mnErrCode |= ERROR_FAILSAFE;
+	if (hrtimer_active(&pTAS2557->mtimer))
+		hrtimer_cancel(&pTAS2557->mtimer);
+
+	if(pTAS2557->mnRestart < RESTART_MAX)
+	{
+		pTAS2557->mnRestart ++;
+		msleep(100);
+		dev_err(pTAS2557->dev, "I2C COMM error, restart SmartAmp.\n");
+		schedule_delayed_work(&pTAS2557->irq_work, msecs_to_jiffies(100));
+		return;
+	}
+	pTAS2557->enableIRQ(pTAS2557, false, false);
+	ret = tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_data);
+	if (ret < 0)
+		dev_dbg(pTAS2557->dev, "failed load shutdown\n");
+
+	pTAS2557->mbPowerUp = false;
+	pTAS2557->hw_reset(pTAS2557);
+	ret = pTAS2557->write(pTAS2557, TAS2557_SW_RESET_REG, 0x01);
+	if (ret < 0)
+		dev_dbg(pTAS2557->dev, "failed sw reset\n");
+
+	udelay(1000);
+	ret = pTAS2557->write(pTAS2557, TAS2557_SPK_CTRL_REG, 0x04);
+	if (ret < 0)
+		dev_dbg(pTAS2557->dev, "failed in spk ctrl\n");
+	if (pTAS2557->mpFirmware != NULL)
+		tas2557_clear_firmware(pTAS2557->mpFirmware);
+
+	pTAS2557->mpFirmware->mnPrograms = 0;
+}
+
+int tas2557_checkPLL(struct tas2557_priv *pTAS2557)
+{
+	int nResult = 0;
+/*
+* TO DO
+*/
+
+	return nResult;
+}
+
+/*
+* tas2557_load_coefficient
+*/
+static int tas2557_load_coefficient(struct tas2557_priv *pTAS2557,
+	int nPrevConfig, int nNewConfig, bool bPowerOn)
+{
+	int nResult = 0;
+	struct TPLL *pPLL;
+	struct TProgram *pProgram;
+	struct TConfiguration *pPrevConfiguration;
+	struct TConfiguration *pNewConfiguration;
+	bool bRestorePower = false;
+
+	if (!pTAS2557->mpFirmware->mnConfigurations) {
+		dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__);
+		goto end;
+	}
+
+	if (nNewConfig >= pTAS2557->mpFirmware->mnConfigurations) {
+		dev_err(pTAS2557->dev, "%s, invalid configuration New=%d, total=%d\n",
+			__func__, nNewConfig, pTAS2557->mpFirmware->mnConfigurations);
+		goto end;
+	}
+
+	if (nPrevConfig < 0)
+		pPrevConfiguration = NULL;
+	else if (nPrevConfig == nNewConfig) {
+		dev_dbg(pTAS2557->dev, "%s, config [%d] already loaded\n",
+			__func__, nNewConfig);
+		goto end;
+	} else
+		pPrevConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[nPrevConfig]);
+
+	pNewConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[nNewConfig]);
+	pTAS2557->mnCurrentConfiguration = nNewConfig;
+	if (pPrevConfiguration) {
+		if (pPrevConfiguration->mnPLL == pNewConfiguration->mnPLL) {
+			dev_dbg(pTAS2557->dev, "%s, PLL same\n", __func__);
+			goto prog_coefficient;
+		}
+	}
+
+	pProgram = &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]);
+	if (bPowerOn) {
+		dev_dbg(pTAS2557->dev, "%s, power down to load new PLL\n", __func__);
+		if (hrtimer_active(&pTAS2557->mtimer))
+			hrtimer_cancel(&pTAS2557->mtimer);
+
+		if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE)
+			pTAS2557->enableIRQ(pTAS2557, false, false);
+
+		nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_data);
+		if (nResult < 0)
+			goto end;
+		bRestorePower = true;
+	}
+
+	/* load PLL */
+	pPLL = &(pTAS2557->mpFirmware->mpPLLs[pNewConfiguration->mnPLL]);
+	dev_dbg(pTAS2557->dev, "load PLL: %s block for Configuration %s\n",
+		pPLL->mpName, pNewConfiguration->mpName);
+	nResult = tas2557_load_block(pTAS2557, &(pPLL->mBlock));
+	if (nResult < 0)
+		goto end;
+	pTAS2557->mnCurrentSampleRate = pNewConfiguration->mnSamplingRate;
+
+	dev_dbg(pTAS2557->dev, "load configuration %s conefficient pre block\n",
+		pNewConfiguration->mpName);
+	nResult = tas2557_load_data(pTAS2557, &(pNewConfiguration->mData), TAS2557_BLOCK_CFG_PRE_DEV_A);
+	if (nResult < 0)
+		goto end;
+
+prog_coefficient:
+	dev_dbg(pTAS2557->dev, "load new configuration: %s, coeff block data\n",
+		pNewConfiguration->mpName);
+	nResult = tas2557_load_data(pTAS2557, &(pNewConfiguration->mData),
+		TAS2557_BLOCK_CFG_COEFF_DEV_A);
+	if (nResult < 0)
+		goto end;
+
+	if (pTAS2557->mpCalFirmware->mnCalibrations) {
+		nResult = tas2557_set_calibration(pTAS2557, pTAS2557->mnCurrentCalibration);
+		if (nResult < 0)
+			goto end;
+	}
+
+	if (bRestorePower) {
+		pTAS2557->clearIRQ(pTAS2557);
+		dev_dbg(pTAS2557->dev, "device powered up, load startup\n");
+		nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_startup_data);
+		if (nResult < 0)
+			goto end;
+		if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) {
+			nResult = tas2557_checkPLL(pTAS2557);
+			if (nResult < 0) {
+				nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_data);
+				pTAS2557->mbPowerUp = false;
+				goto end;
+			}
+		}
+		dev_dbg(pTAS2557->dev,
+			"device powered up, load unmute\n");
+		nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_unmute_data);
+		if (nResult < 0)
+			goto end;
+		if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) {
+			pTAS2557->enableIRQ(pTAS2557, true, true);
+			if (!hrtimer_active(&pTAS2557->mtimer)) {
+				pTAS2557->mnDieTvReadCounter = 0;
+				hrtimer_start(&pTAS2557->mtimer,
+					ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+			}
+		}
+	}
+end:
+
+	pTAS2557->mnNewConfiguration = pTAS2557->mnCurrentConfiguration;
+	return nResult;
+}
+
+int tas2557_update_edge(struct tas2557_priv *pTAS2557)
+{
+	int nResult = 0;
+	dev_dbg(pTAS2557->dev,
+		"%s, edge: %d\n",
+		__func__, pTAS2557->mnEdge);
+
+	nResult = pTAS2557->update_bits(pTAS2557, TAS2557_SPK_CTRL_REG, 0x7, pTAS2557->mnEdge);
+
+	return nResult;
+}
+
+int tas2557_enable(struct tas2557_priv *pTAS2557, bool bEnable)
+{
+	int nResult = 0;
+	unsigned int nValue;
+	const char *pFWName;
+	struct TProgram *pProgram;
+
+	dev_dbg(pTAS2557->dev, "Enable: %d\n", bEnable);
+
+	if ((pTAS2557->mpFirmware->mnPrograms == 0)
+		|| (pTAS2557->mpFirmware->mnConfigurations == 0)) {
+		dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__);
+		/*Load firmware*/
+		if (pTAS2557->mnPGID == TAS2557_PG_VERSION_2P1) {
+			dev_info(pTAS2557->dev, "PG2.1 Silicon found\n");
+			pFWName = TAS2557_FW_NAME;
+		} else if (pTAS2557->mnPGID == TAS2557_PG_VERSION_1P0) {
+			dev_info(pTAS2557->dev, "PG1.0 Silicon found\n");
+			pFWName = TAS2557_PG1P0_FW_NAME;
+		} else {
+			nResult = -ENOTSUPP;
+			dev_info(pTAS2557->dev, "unsupport Silicon 0x%x\n", pTAS2557->mnPGID);
+			goto end;
+		}
+		nResult = request_firmware_nowait(THIS_MODULE, 1, pFWName,
+			pTAS2557->dev, GFP_KERNEL, pTAS2557, tas2557_fw_ready);
+		if(nResult < 0)
+			goto end;
+		dev_err(pTAS2557->dev, "%s, firmware is loaded\n", __func__);
+	}
+
+	/* check safe guard*/
+	nResult = pTAS2557->read(pTAS2557, TAS2557_SAFE_GUARD_REG, &nValue);
+	if (nResult < 0)
+		goto end;
+	if ((nValue&0xff) != TAS2557_SAFE_GUARD_PATTERN) {
+		dev_err(pTAS2557->dev, "ERROR safe guard failure!\n");
+		nResult = -EPIPE;
+		pTAS2557->mnErrCode = ERROR_SAFE_GUARD;
+		pTAS2557->mbPowerUp = true;
+		goto end;
+	}
+
+	pProgram = &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]);
+	if (bEnable) {
+		if (!pTAS2557->mbPowerUp) {
+			if (!pTAS2557->mbCalibrationLoaded) {
+				tas2557_set_calibration(pTAS2557, 0xFF);
+				pTAS2557->mbCalibrationLoaded = true;
+			}
+
+			if (pTAS2557->mbLoadConfigurationPrePowerUp) {
+				dev_dbg(pTAS2557->dev, "load coefficient before power\n");
+				pTAS2557->mbLoadConfigurationPrePowerUp = false;
+				nResult = tas2557_load_coefficient(pTAS2557,
+					pTAS2557->mnCurrentConfiguration, pTAS2557->mnNewConfiguration, false);
+				if (nResult < 0)
+					goto end;
+			}
+
+			pTAS2557->clearIRQ(pTAS2557);
+			/* power on device */
+			dev_dbg(pTAS2557->dev, "Enable: load startup sequence\n");
+			nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_startup_data);
+			if (nResult < 0)
+				goto end;
+			if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) {
+				nResult = tas2557_checkPLL(pTAS2557);
+				if (nResult < 0) {
+					nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_data);
+					goto end;
+				}
+			}
+			dev_dbg(pTAS2557->dev, "Enable: load unmute sequence\n");
+			nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_unmute_data);
+			if (nResult < 0)
+				goto end;
+
+			pTAS2557->mbPowerUp = true;
+
+			tas2557_get_die_temperature(pTAS2557, &nValue);
+			if(nValue == 0x80000000)
+			{
+				dev_err(pTAS2557->dev, "%s, thermal sensor is wrong, mute output\n", __func__);
+				nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_data);
+				pTAS2557->mbPowerUp = false;
+				goto end;
+			}
+
+			if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) {
+				/* turn on IRQ */
+				pTAS2557->enableIRQ(pTAS2557, true, true);
+				if (!hrtimer_active(&pTAS2557->mtimer)) {
+					pTAS2557->mnDieTvReadCounter = 0;
+					hrtimer_start(&pTAS2557->mtimer,
+						ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+				}
+			}
+			pTAS2557->mnRestart = 0;
+		}
+	} else {
+		if (pTAS2557->mbPowerUp) {
+			if (hrtimer_active(&pTAS2557->mtimer))
+				hrtimer_cancel(&pTAS2557->mtimer);
+
+			dev_dbg(pTAS2557->dev, "Enable: load shutdown sequence\n");
+			if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) {
+				/* turn off IRQ */
+				pTAS2557->enableIRQ(pTAS2557, false, false);
+			}
+			nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_data);
+			if (nResult < 0)
+				goto end;
+
+			pTAS2557->mbPowerUp = false;
+			pTAS2557->mnRestart = 0;
+		}
+	}
+
+	nResult = 0;
+
+end:
+	if (nResult < 0) {
+		if (pTAS2557->mnErrCode & (ERROR_DEVA_I2C_COMM | ERROR_PRAM_CRCCHK | ERROR_YRAM_CRCCHK | ERROR_SAFE_GUARD))
+			failsafe(pTAS2557);
+	}
+
+	return nResult;
+}
+
+int tas2557_set_sampling_rate(struct tas2557_priv *pTAS2557, unsigned int nSamplingRate)
+{
+	int nResult = 0;
+	struct TConfiguration *pConfiguration;
+	unsigned int nConfiguration;
+
+	dev_dbg(pTAS2557->dev, "tas2557_setup_clocks: nSamplingRate = %d [Hz]\n",
+		nSamplingRate);
+
+	if ((!pTAS2557->mpFirmware->mpPrograms) ||
+		(!pTAS2557->mpFirmware->mpConfigurations)) {
+		dev_err(pTAS2557->dev, "Firmware not loaded\n");
+		nResult = -EINVAL;
+		goto end;
+	}
+
+	pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]);
+	if (pConfiguration->mnSamplingRate == nSamplingRate) {
+		dev_info(pTAS2557->dev, "Sampling rate for current configuration matches: %d\n",
+			nSamplingRate);
+		nResult = 0;
+		goto end;
+	}
+
+	for (nConfiguration = 0;
+		nConfiguration < pTAS2557->mpFirmware->mnConfigurations;
+		nConfiguration++) {
+		pConfiguration =
+			&(pTAS2557->mpFirmware->mpConfigurations[nConfiguration]);
+		if ((pConfiguration->mnSamplingRate == nSamplingRate)
+			&& (pConfiguration->mnProgram == pTAS2557->mnCurrentProgram)) {
+			dev_info(pTAS2557->dev,
+				"Found configuration: %s, with compatible sampling rate %d\n",
+				pConfiguration->mpName, nSamplingRate);
+			nResult = tas2557_load_configuration(pTAS2557, nConfiguration, false);
+			goto end;
+		}
+	}
+
+	dev_err(pTAS2557->dev, "Cannot find a configuration that supports sampling rate: %d\n",
+		nSamplingRate);
+
+end:
+
+	return nResult;
+}
+
+static void fw_print_header(struct tas2557_priv *pTAS2557, struct TFirmware *pFirmware)
+{
+	dev_info(pTAS2557->dev, "FW Size       = %d", pFirmware->mnFWSize);
+	dev_info(pTAS2557->dev, "Checksum      = 0x%04X", pFirmware->mnChecksum);
+	dev_info(pTAS2557->dev, "PPC Version   = 0x%04X", pFirmware->mnPPCVersion);
+	dev_info(pTAS2557->dev, "FW  Version    = 0x%04X", pFirmware->mnFWVersion);
+	dev_info(pTAS2557->dev, "Driver Version= 0x%04X", pFirmware->mnDriverVersion);
+	dev_info(pTAS2557->dev, "Timestamp     = %d", pFirmware->mnTimeStamp);
+	dev_info(pTAS2557->dev, "DDC Name      = %s", pFirmware->mpDDCName);
+	dev_info(pTAS2557->dev, "Description   = %s", pFirmware->mpDescription);
+}
+
+inline unsigned int fw_convert_number(unsigned char *pData)
+{
+	return pData[3] + (pData[2] << 8) + (pData[1] << 16) + (pData[0] << 24);
+}
+
+static int fw_parse_header(struct tas2557_priv *pTAS2557,
+	struct TFirmware *pFirmware, unsigned char *pData, unsigned int nSize)
+{
+	unsigned char *pDataStart = pData;
+	unsigned int n;
+	unsigned char pMagicNumber[] = { 0x35, 0x35, 0x35, 0x32 };
+
+	if (nSize < 104) {
+		dev_err(pTAS2557->dev, "Firmware: Header too short");
+		return -EINVAL;
+	}
+
+	if (memcmp(pData, pMagicNumber, 4)) {
+		dev_err(pTAS2557->dev, "Firmware: Magic number doesn't match");
+		return -EINVAL;
+	}
+	pData += 4;
+
+	pFirmware->mnFWSize = fw_convert_number(pData);
+	pData += 4;
+
+	pFirmware->mnChecksum = fw_convert_number(pData);
+	pData += 4;
+
+	pFirmware->mnPPCVersion = fw_convert_number(pData);
+	pData += 4;
+
+	pFirmware->mnFWVersion = fw_convert_number(pData);
+	pData += 4;
+
+	pFirmware->mnDriverVersion = fw_convert_number(pData);
+	pData += 4;
+
+	pFirmware->mnTimeStamp = fw_convert_number(pData);
+	pData += 4;
+
+	memcpy(pFirmware->mpDDCName, pData, 64);
+	pData += 64;
+
+	n = strlen(pData);
+	pFirmware->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+	pData += n + 1;
+	if ((pData - pDataStart) >= nSize) {
+		dev_err(pTAS2557->dev, "Firmware: Header too short after DDC description");
+		return -EINVAL;
+	}
+
+	pFirmware->mnDeviceFamily = fw_convert_number(pData);
+	pData += 4;
+	if (pFirmware->mnDeviceFamily != 0) {
+		dev_err(pTAS2557->dev,
+			"deviceFamily %d, not TAS device", pFirmware->mnDeviceFamily);
+		return -EINVAL;
+	}
+
+	pFirmware->mnDevice = fw_convert_number(pData);
+	pData += 4;
+
+	if (pFirmware->mnDevice != 2) {
+		dev_err(pTAS2557->dev,
+			"device %d, not TAS2557 Dual Mono", pFirmware->mnDevice);
+		return -EINVAL;
+	}
+
+	fw_print_header(pTAS2557, pFirmware);
+	return pData - pDataStart;
+}
+
+static int fw_parse_block_data(struct tas2557_priv *pTAS2557, struct TFirmware *pFirmware,
+	struct TBlock *pBlock, unsigned char *pData)
+{
+	unsigned char *pDataStart = pData;
+	unsigned int n;
+
+	pBlock->mnType = fw_convert_number(pData);
+	pData += 4;
+
+	if (pFirmware->mnDriverVersion >= PPC_DRIVER_CRCCHK) {
+		pBlock->mbPChkSumPresent = pData[0];
+		pData++;
+
+		pBlock->mnPChkSum = pData[0];
+		pData++;
+
+		pBlock->mbYChkSumPresent = pData[0];
+		pData++;
+
+		pBlock->mnYChkSum = pData[0];
+		pData++;
+	} else {
+		pBlock->mbPChkSumPresent = 0;
+		pBlock->mbYChkSumPresent = 0;
+	}
+
+	pBlock->mnCommands = fw_convert_number(pData);
+	pData += 4;
+
+	n = pBlock->mnCommands * 4;
+	pBlock->mpData = kmemdup(pData, n, GFP_KERNEL);
+	pData += n;
+	return pData - pDataStart;
+}
+
+static int fw_parse_data(struct tas2557_priv *pTAS2557, struct TFirmware *pFirmware,
+	struct TData *pImageData, unsigned char *pData)
+{
+	unsigned char *pDataStart = pData;
+	unsigned int nBlock;
+	unsigned int n;
+
+	memcpy(pImageData->mpName, pData, 64);
+	pData += 64;
+
+	n = strlen(pData);
+	pImageData->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+	pData += n + 1;
+
+	pImageData->mnBlocks = (pData[0] << 8) + pData[1];
+	pData += 2;
+
+	pImageData->mpBlocks =
+		kmalloc(sizeof(struct TBlock) * pImageData->mnBlocks, GFP_KERNEL);
+        if(pImageData->mpBlocks == NULL)
+        {
+                dev_dbg(pTAS2557->dev, "failed malloc blocks mem\n");
+                goto end;
+        }
+
+	for (nBlock = 0; nBlock < pImageData->mnBlocks; nBlock++) {
+		n = fw_parse_block_data(pTAS2557, pFirmware,
+			&(pImageData->mpBlocks[nBlock]), pData);
+		pData += n;
+	}
+
+end:	//[FairPhone][Audio][jinjia]=2020.05.19=Fix the build error with TI sample code.
+	return pData - pDataStart;
+}
+
+static int fw_parse_pll_data(struct tas2557_priv *pTAS2557,
+	struct TFirmware *pFirmware, unsigned char *pData)
+{
+	unsigned char *pDataStart = pData;
+	unsigned int n;
+	unsigned int nPLL;
+	struct TPLL *pPLL;
+
+	pFirmware->mnPLLs = (pData[0] << 8) + pData[1];
+	pData += 2;
+
+	if (pFirmware->mnPLLs == 0)
+		goto end;
+
+	pFirmware->mpPLLs = kmalloc_array(pFirmware->mnPLLs, sizeof(struct TPLL), GFP_KERNEL);
+	for (nPLL = 0; nPLL < pFirmware->mnPLLs; nPLL++) {
+		pPLL = &(pFirmware->mpPLLs[nPLL]);
+
+		memcpy(pPLL->mpName, pData, 64);
+		pData += 64;
+
+		n = strlen(pData);
+		pPLL->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+		pData += n + 1;
+
+		n = fw_parse_block_data(pTAS2557, pFirmware, &(pPLL->mBlock), pData);
+		pData += n;
+	}
+
+end:
+	return pData - pDataStart;
+}
+
+static int fw_parse_program_data(struct tas2557_priv *pTAS2557,
+	struct TFirmware *pFirmware, unsigned char *pData)
+{
+	unsigned char *pDataStart = pData;
+	unsigned int n;
+	unsigned int nProgram;
+	struct TProgram *pProgram;
+
+	pFirmware->mnPrograms = (pData[0] << 8) + pData[1];
+	pData += 2;
+
+	if (pFirmware->mnPrograms == 0)
+		goto end;
+
+	pFirmware->mpPrograms =
+		kmalloc(sizeof(struct TProgram) * pFirmware->mnPrograms, GFP_KERNEL);
+	if(pFirmware->mpPrograms == NULL)
+	{
+		dev_dbg(pTAS2557->dev, "failed malloc program mem\n");
+		goto end;
+	}
+
+	for (nProgram = 0; nProgram < pFirmware->mnPrograms; nProgram++) {
+		pProgram = &(pFirmware->mpPrograms[nProgram]);
+		memcpy(pProgram->mpName, pData, 64);
+		pData += 64;
+
+		n = strlen(pData);
+		pProgram->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+		pData += n + 1;
+
+		pProgram->mnAppMode = pData[0];
+		pData++;
+
+		pProgram->mnBoost = (pData[0] << 8) + pData[1];
+		pData += 2;
+
+		n = fw_parse_data(pTAS2557, pFirmware, &(pProgram->mData), pData);
+		pData += n;
+	}
+
+end:
+
+	return pData - pDataStart;
+}
+
+static int fw_parse_configuration_data(struct tas2557_priv *pTAS2557,
+	struct TFirmware *pFirmware, unsigned char *pData)
+{
+	unsigned char *pDataStart = pData;
+	unsigned int n;
+	unsigned int nConfiguration;
+	struct TConfiguration *pConfiguration;
+
+	pFirmware->mnConfigurations = (pData[0] << 8) + pData[1];
+	pData += 2;
+
+	if (pFirmware->mnConfigurations == 0)
+		goto end;
+
+	pFirmware->mpConfigurations =
+		kmalloc(sizeof(struct TConfiguration) * pFirmware->mnConfigurations,
+		GFP_KERNEL);
+        if(pFirmware->mpConfigurations == NULL)
+        {
+                dev_dbg(pTAS2557->dev, "failed malloc configuration mem\n");
+                goto end;
+        }
+
+	for (nConfiguration = 0; nConfiguration < pFirmware->mnConfigurations;
+		nConfiguration++) {
+		pConfiguration = &(pFirmware->mpConfigurations[nConfiguration]);
+		memcpy(pConfiguration->mpName, pData, 64);
+		pData += 64;
+
+		n = strlen(pData);
+		pConfiguration->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+		pData += n + 1;
+
+		if ((pFirmware->mnDriverVersion >= PPC_DRIVER_CONFDEV)
+			|| ((pFirmware->mnDriverVersion >= PPC_DRIVER_CFGDEV_NONCRC)
+				&& (pFirmware->mnDriverVersion < PPC_DRIVER_CRCCHK))) {
+			pConfiguration->mnDevices = (pData[0] << 8) + pData[1];
+			pData += 2;
+		} else
+			pConfiguration->mnDevices = 1;
+
+		pConfiguration->mnProgram = pData[0];
+		pData++;
+
+		pConfiguration->mnPLL = pData[0];
+		pData++;
+
+		pConfiguration->mnSamplingRate = fw_convert_number(pData);
+		pData += 4;
+
+		if (pFirmware->mnDriverVersion >= PPC_DRIVER_MTPLLSRC) {
+			pConfiguration->mnPLLSrc = pData[0];
+			pData++;
+
+			pConfiguration->mnPLLSrcRate = fw_convert_number(pData);
+			pData += 4;
+		}
+
+		n = fw_parse_data(pTAS2557, pFirmware, &(pConfiguration->mData), pData);
+		pData += n;
+	}
+
+end:
+
+	return pData - pDataStart;
+}
+
+int fw_parse_calibration_data(struct tas2557_priv *pTAS2557,
+	struct TFirmware *pFirmware, unsigned char *pData)
+{
+	unsigned char *pDataStart = pData;
+	unsigned int n;
+	unsigned int nCalibration;
+	struct TCalibration *pCalibration;
+
+	pFirmware->mnCalibrations = (pData[0] << 8) + pData[1];
+	pData += 2;
+
+	if (pFirmware->mnCalibrations == 0)
+		goto end;
+
+	pFirmware->mpCalibrations =
+		kmalloc(sizeof(struct TCalibration) * pFirmware->mnCalibrations, GFP_KERNEL);
+	if(pFirmware->mpCalibrations == NULL)
+	{
+		dev_err(pTAS2557->dev, "failed to malloc calibration mem\n");
+		goto end;
+	}
+
+	for (nCalibration = 0;
+		nCalibration < pFirmware->mnCalibrations;
+		nCalibration++) {
+		pCalibration = &(pFirmware->mpCalibrations[nCalibration]);
+		memcpy(pCalibration->mpName, pData, 64);
+		pData += 64;
+
+		n = strlen(pData);
+		pCalibration->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+		pData += n + 1;
+
+		pCalibration->mnProgram = pData[0];
+		pData++;
+
+		pCalibration->mnConfiguration = pData[0];
+		pData++;
+
+		n = fw_parse_data(pTAS2557, pFirmware, &(pCalibration->mData), pData);
+		pData += n;
+	}
+
+end:
+
+	return pData - pDataStart;
+}
+
+static int fw_parse(struct tas2557_priv *pTAS2557,
+	struct TFirmware *pFirmware, unsigned char *pData, unsigned int nSize)
+{
+	int nPosition = 0;
+
+	nPosition = fw_parse_header(pTAS2557, pFirmware, pData, nSize);
+	if (nPosition < 0) {
+		dev_err(pTAS2557->dev, "Firmware: Wrong Header");
+		return -EINVAL;
+	}
+
+	if (nPosition >= nSize) {
+		dev_err(pTAS2557->dev, "Firmware: Too short");
+		return -EINVAL;
+	}
+
+	pData += nPosition;
+	nSize -= nPosition;
+	nPosition = 0;
+
+	nPosition = fw_parse_pll_data(pTAS2557, pFirmware, pData);
+
+	pData += nPosition;
+	nSize -= nPosition;
+	nPosition = 0;
+
+	nPosition = fw_parse_program_data(pTAS2557, pFirmware, pData);
+
+	pData += nPosition;
+	nSize -= nPosition;
+	nPosition = 0;
+
+	nPosition = fw_parse_configuration_data(pTAS2557, pFirmware, pData);
+
+	pData += nPosition;
+	nSize -= nPosition;
+	nPosition = 0;
+
+	if (nSize > 64)
+		nPosition = fw_parse_calibration_data(pTAS2557, pFirmware, pData);
+	return 0;
+}
+
+
+static const unsigned char crc8_lookup_table[CRC8_TABLE_SIZE] = {
+0x00, 0x4D, 0x9A, 0xD7, 0x79, 0x34, 0xE3, 0xAE, 0xF2, 0xBF, 0x68, 0x25, 0x8B, 0xC6, 0x11, 0x5C,
+0xA9, 0xE4, 0x33, 0x7E, 0xD0, 0x9D, 0x4A, 0x07, 0x5B, 0x16, 0xC1, 0x8C, 0x22, 0x6F, 0xB8, 0xF5,
+0x1F, 0x52, 0x85, 0xC8, 0x66, 0x2B, 0xFC, 0xB1, 0xED, 0xA0, 0x77, 0x3A, 0x94, 0xD9, 0x0E, 0x43,
+0xB6, 0xFB, 0x2C, 0x61, 0xCF, 0x82, 0x55, 0x18, 0x44, 0x09, 0xDE, 0x93, 0x3D, 0x70, 0xA7, 0xEA,
+0x3E, 0x73, 0xA4, 0xE9, 0x47, 0x0A, 0xDD, 0x90, 0xCC, 0x81, 0x56, 0x1B, 0xB5, 0xF8, 0x2F, 0x62,
+0x97, 0xDA, 0x0D, 0x40, 0xEE, 0xA3, 0x74, 0x39, 0x65, 0x28, 0xFF, 0xB2, 0x1C, 0x51, 0x86, 0xCB,
+0x21, 0x6C, 0xBB, 0xF6, 0x58, 0x15, 0xC2, 0x8F, 0xD3, 0x9E, 0x49, 0x04, 0xAA, 0xE7, 0x30, 0x7D,
+0x88, 0xC5, 0x12, 0x5F, 0xF1, 0xBC, 0x6B, 0x26, 0x7A, 0x37, 0xE0, 0xAD, 0x03, 0x4E, 0x99, 0xD4,
+0x7C, 0x31, 0xE6, 0xAB, 0x05, 0x48, 0x9F, 0xD2, 0x8E, 0xC3, 0x14, 0x59, 0xF7, 0xBA, 0x6D, 0x20,
+0xD5, 0x98, 0x4F, 0x02, 0xAC, 0xE1, 0x36, 0x7B, 0x27, 0x6A, 0xBD, 0xF0, 0x5E, 0x13, 0xC4, 0x89,
+0x63, 0x2E, 0xF9, 0xB4, 0x1A, 0x57, 0x80, 0xCD, 0x91, 0xDC, 0x0B, 0x46, 0xE8, 0xA5, 0x72, 0x3F,
+0xCA, 0x87, 0x50, 0x1D, 0xB3, 0xFE, 0x29, 0x64, 0x38, 0x75, 0xA2, 0xEF, 0x41, 0x0C, 0xDB, 0x96,
+0x42, 0x0F, 0xD8, 0x95, 0x3B, 0x76, 0xA1, 0xEC, 0xB0, 0xFD, 0x2A, 0x67, 0xC9, 0x84, 0x53, 0x1E,
+0xEB, 0xA6, 0x71, 0x3C, 0x92, 0xDF, 0x08, 0x45, 0x19, 0x54, 0x83, 0xCE, 0x60, 0x2D, 0xFA, 0xB7,
+0x5D, 0x10, 0xC7, 0x8A, 0x24, 0x69, 0xBE, 0xF3, 0xAF, 0xE2, 0x35, 0x78, 0xD6, 0x9B, 0x4C, 0x01,
+0xF4, 0xB9, 0x6E, 0x23, 0x8D, 0xC0, 0x17, 0x5A, 0x06, 0x4B, 0x9C, 0xD1, 0x7F, 0x32, 0xE5, 0xA8
+};
+
+static int isInPageYRAM(struct tas2557_priv *pTAS2557, struct TYCRC *pCRCData,
+	unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len)
+{
+	int nResult = 0;
+
+	if (nBook == TAS2557_YRAM_BOOK1) {
+		if (nPage == TAS2557_YRAM1_PAGE) {
+			if (nReg >= TAS2557_YRAM1_START_REG) {
+				pCRCData->mnOffset = nReg;
+				pCRCData->mnLen = len;
+				nResult = 1;
+			} else if ((nReg + len) > TAS2557_YRAM1_START_REG) {
+				pCRCData->mnOffset = TAS2557_YRAM1_START_REG;
+				pCRCData->mnLen = len - (TAS2557_YRAM1_START_REG - nReg);
+				nResult = 1;
+			} else
+				nResult = 0;
+		} else if (nPage == TAS2557_YRAM3_PAGE) {
+			if (nReg > TAS2557_YRAM3_END_REG) {
+				nResult = 0;
+			} else if (nReg >= TAS2557_YRAM3_START_REG) {
+				if ((nReg + len) > TAS2557_YRAM3_END_REG) {
+					pCRCData->mnOffset = nReg;
+					pCRCData->mnLen = TAS2557_YRAM3_END_REG - nReg + 1;
+					nResult = 1;
+				} else {
+					pCRCData->mnOffset = nReg;
+					pCRCData->mnLen = len;
+					nResult = 1;
+				}
+			} else {
+				if ((nReg + (len - 1)) < TAS2557_YRAM3_START_REG)
+					nResult = 0;
+				else {
+					pCRCData->mnOffset = TAS2557_YRAM3_START_REG;
+					pCRCData->mnLen = len - (TAS2557_YRAM3_START_REG - nReg);
+					nResult = 1;
+				}
+			}
+		}
+	} else if (nBook == TAS2557_YRAM_BOOK2) {
+		if (nPage == TAS2557_YRAM5_PAGE) {
+			if (nReg > TAS2557_YRAM5_END_REG) {
+				nResult = 0;
+			} else if (nReg >= TAS2557_YRAM5_START_REG) {
+				if ((nReg + len) > TAS2557_YRAM5_END_REG) {
+					pCRCData->mnOffset = nReg;
+					pCRCData->mnLen = TAS2557_YRAM5_END_REG - nReg + 1;
+					nResult = 1;
+				} else {
+					pCRCData->mnOffset = nReg;
+					pCRCData->mnLen = len;
+					nResult = 1;
+				}
+			} else {
+				if ((nReg + (len - 1)) < TAS2557_YRAM5_START_REG)
+					nResult = 0;
+				else {
+					pCRCData->mnOffset = TAS2557_YRAM5_START_REG;
+					pCRCData->mnLen = len - (TAS2557_YRAM5_START_REG - nReg);
+					nResult = 1;
+				}
+			}
+		}
+	} else
+		nResult = 0;
+
+	return nResult;
+}
+
+static int isInBlockYRAM(struct tas2557_priv *pTAS2557, struct TYCRC *pCRCData,
+	unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len)
+{
+	int nResult;
+
+	if (nBook == TAS2557_YRAM_BOOK1) {
+		if (nPage < TAS2557_YRAM2_START_PAGE)
+			nResult = 0;
+		else if (nPage <= TAS2557_YRAM2_END_PAGE) {
+			if (nReg > TAS2557_YRAM2_END_REG)
+				nResult = 0;
+			else if (nReg >= TAS2557_YRAM2_START_REG) {
+				pCRCData->mnOffset = nReg;
+				pCRCData->mnLen = len;
+				nResult = 1;
+			} else {
+				if ((nReg + (len - 1)) < TAS2557_YRAM2_START_REG)
+					nResult = 0;
+				else {
+					pCRCData->mnOffset = TAS2557_YRAM2_START_REG;
+					pCRCData->mnLen = nReg + len - TAS2557_YRAM2_START_REG;
+					nResult = 1;
+				}
+			}
+		} else
+			nResult = 0;
+	} else if (nBook == TAS2557_YRAM_BOOK2) {
+		if (nPage < TAS2557_YRAM4_START_PAGE)
+			nResult = 0;
+		else if (nPage <= TAS2557_YRAM4_END_PAGE) {
+			if (nReg > TAS2557_YRAM2_END_REG)
+				nResult = 0;
+			else if (nReg >= TAS2557_YRAM2_START_REG) {
+				pCRCData->mnOffset = nReg;
+				pCRCData->mnLen = len;
+				nResult = 1;
+			} else {
+				if ((nReg + (len - 1)) < TAS2557_YRAM2_START_REG)
+					nResult = 0;
+				else {
+					pCRCData->mnOffset = TAS2557_YRAM2_START_REG;
+					pCRCData->mnLen = nReg + len - TAS2557_YRAM2_START_REG;
+					nResult = 1;
+				}
+			}
+		} else
+			nResult = 0;
+	} else
+		nResult = 0;
+
+	return nResult;
+}
+
+
+static int isYRAM(struct tas2557_priv *pTAS2557, struct TYCRC *pCRCData,
+	unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len)
+{
+	int nResult;
+
+	nResult = isInPageYRAM(pTAS2557, pCRCData, nBook, nPage, nReg, len);
+
+	if (nResult == 0)
+		nResult = isInBlockYRAM(pTAS2557, pCRCData, nBook, nPage, nReg, len);
+
+	return nResult;
+}
+
+/*
+ * crc8 - calculate a crc8 over the given input data.
+ *
+ * table: crc table used for calculation.
+ * pdata: pointer to data buffer.
+ * nbytes: number of bytes in data buffer.
+ * crc:	previous returned crc8 value.
+ */
+static u8 ti_crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata, size_t nbytes, u8 crc)
+{
+	/* loop over the buffer data */
+	while (nbytes-- > 0)
+		crc = table[(crc ^ *pdata++) & 0xff];
+
+	return crc;
+}
+
+static int doSingleRegCheckSum(struct tas2557_priv *pTAS2557,
+	unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char nValue)
+{
+	int nResult = 0;
+	struct TYCRC sCRCData;
+	unsigned int nData1 = 0;
+
+	if ((nBook == TAS2557_BOOK_ID(TAS2557_SA_COEFF_SWAP_REG))
+		&& (nPage == TAS2557_PAGE_ID(TAS2557_SA_COEFF_SWAP_REG))
+		&& (nReg >= TAS2557_PAGE_REG(TAS2557_SA_COEFF_SWAP_REG))
+		&& (nReg <= (TAS2557_PAGE_REG(TAS2557_SA_COEFF_SWAP_REG) + 4))) {
+		/* DSP swap command, pass */
+		nResult = 0;
+		goto end;
+	}
+
+	nResult = isYRAM(pTAS2557, &sCRCData, nBook, nPage, nReg, 1);
+	if (nResult == 1) {
+		nResult = pTAS2557->read(pTAS2557, TAS2557_REG(nBook, nPage, nReg), &nData1);
+		if (nResult < 0)
+			goto end;
+
+		if (nData1 != nValue) {
+			dev_err(pTAS2557->dev, "error2 (line %d),B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n",
+				__LINE__, nBook, nPage, nReg, nValue, nData1);
+			nResult = -EAGAIN;
+			goto end;
+		}
+
+		nResult = ti_crc8(crc8_lookup_table, &nValue, 1, 0);
+	}
+
+end:
+
+	return nResult;
+}
+
+static int doMultiRegCheckSum(struct tas2557_priv *pTAS2557,
+	unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned int len)
+{
+	int nResult = 0, i;
+	unsigned char nCRCChkSum = 0;
+	unsigned char nBuf1[128];
+	struct TYCRC TCRCData;
+
+	if ((nReg + len-1) > 127) {
+		nResult = -EINVAL;
+		dev_err(pTAS2557->dev, "firmware error\n");
+		goto end;
+	}
+
+	if ((nBook == TAS2557_BOOK_ID(TAS2557_SA_COEFF_SWAP_REG))
+		&& (nPage == TAS2557_PAGE_ID(TAS2557_SA_COEFF_SWAP_REG))
+		&& (nReg == TAS2557_PAGE_REG(TAS2557_SA_COEFF_SWAP_REG))
+		&& (len == 4)) {
+		/* DSP swap command, pass */
+		nResult = 0;
+		goto end;
+	}
+
+	nResult = isYRAM(pTAS2557, &TCRCData, nBook, nPage, nReg, len);
+	if (nResult == 1) {
+		if (len == 1) {
+			dev_err(pTAS2557->dev, "firmware error\n");
+			nResult = -EINVAL;
+			goto end;
+		} else {
+			nResult = pTAS2557->bulk_read(pTAS2557, TAS2557_REG(nBook, nPage, TCRCData.mnOffset), nBuf1, TCRCData.mnLen);
+			if (nResult < 0)
+				goto end;
+
+			for (i = 0; i < TCRCData.mnLen; i++) {
+				if ((nBook == TAS2557_BOOK_ID(TAS2557_SA_COEFF_SWAP_REG))
+					&& (nPage == TAS2557_PAGE_ID(TAS2557_SA_COEFF_SWAP_REG))
+					&& ((i + TCRCData.mnOffset)
+						>= TAS2557_PAGE_REG(TAS2557_SA_COEFF_SWAP_REG))
+					&& ((i + TCRCData.mnOffset)
+						<= (TAS2557_PAGE_REG(TAS2557_SA_COEFF_SWAP_REG) + 4))) {
+					/* DSP swap command, bypass */
+					continue;
+				} else
+					nCRCChkSum += ti_crc8(crc8_lookup_table, &nBuf1[i], 1, 0);
+			}
+
+			nResult = nCRCChkSum;
+		}
+	}
+
+end:
+
+	return nResult;
+}
+
+static int tas2557_load_block(struct tas2557_priv *pTAS2557, struct TBlock *pBlock)
+{
+	int nResult = 0;
+	unsigned int nCommand = 0;
+	unsigned char nBook;
+	unsigned char nPage;
+	unsigned char nOffset;
+	unsigned char nData;
+	unsigned int nLength;
+	unsigned int nSleep;
+	unsigned char nCRCChkSum = 0;
+	unsigned int nValue1;
+	int nRetry = 6;
+	unsigned char *pData = pBlock->mpData;
+
+	dev_dbg(pTAS2557->dev, "TAS2557 load block: Type = %d, commands = %d\n",
+		pBlock->mnType, pBlock->mnCommands);
+start:
+	if (pBlock->mbPChkSumPresent) {
+		nResult = pTAS2557->write(pTAS2557, TAS2557_CRC_RESET_REG, 1);
+		if (nResult < 0)
+			goto end;
+	}
+
+	if (pBlock->mbYChkSumPresent)
+		nCRCChkSum = 0;
+
+	nCommand = 0;
+
+	while (nCommand < pBlock->mnCommands) {
+		pData = pBlock->mpData + nCommand * 4;
+
+		nBook = pData[0];
+		nPage = pData[1];
+		nOffset = pData[2];
+		nData = pData[3];
+
+		nCommand++;
+
+		if (nOffset <= 0x7F) {
+			nResult = pTAS2557->write(pTAS2557, TAS2557_REG(nBook, nPage, nOffset), nData);
+			if (nResult < 0)
+				goto end;
+			if (pBlock->mbYChkSumPresent) {
+				nResult = doSingleRegCheckSum(pTAS2557, nBook, nPage, nOffset, nData);
+				if (nResult < 0)
+					goto check;
+				nCRCChkSum += (unsigned char)nResult;
+			}
+		} else if (nOffset == 0x81) {
+			nSleep = (nBook << 8) + nPage;
+			msleep(nSleep);
+		} else if (nOffset == 0x85) {
+			pData += 4;
+			nLength = (nBook << 8) + nPage;
+			nBook = pData[0];
+			nPage = pData[1];
+			nOffset = pData[2];
+			if (nLength > 1) {
+				nResult = pTAS2557->bulk_write(pTAS2557, TAS2557_REG(nBook, nPage, nOffset), pData + 3, nLength);
+				if (nResult < 0)
+					goto end;
+				if (pBlock->mbYChkSumPresent) {
+					nResult = doMultiRegCheckSum(pTAS2557, nBook, nPage, nOffset, nLength);
+					if (nResult < 0)
+						goto check;
+					nCRCChkSum += (unsigned char)nResult;
+				}
+			} else {
+				nResult = pTAS2557->write(pTAS2557, TAS2557_REG(nBook, nPage, nOffset), pData[3]);
+				if (nResult < 0)
+					goto end;
+				if (pBlock->mbYChkSumPresent) {
+					nResult = doSingleRegCheckSum(pTAS2557, nBook, nPage, nOffset, pData[3]);
+					if (nResult < 0)
+						goto check;
+					nCRCChkSum += (unsigned char)nResult;
+				}
+			}
+
+			nCommand++;
+
+			if (nLength >= 2)
+				nCommand += ((nLength - 2) / 4) + 1;
+		}
+	}
+	if (pBlock->mbPChkSumPresent) {
+		nResult = pTAS2557->read(pTAS2557, TAS2557_CRC_CHECKSUM_REG, &nValue1);
+		if (nResult < 0)
+			goto end;
+		if ((nValue1&0xff) != pBlock->mnPChkSum) {
+			dev_err(pTAS2557->dev, "Block PChkSum Error: FW = 0x%x, Reg = 0x%x\n",
+				pBlock->mnPChkSum, (nValue1&0xff));
+			nResult = -EAGAIN;
+				pTAS2557->mnErrCode |= ERROR_PRAM_CRCCHK;
+			goto check;
+		}
+
+		nResult = 0;
+		pTAS2557->mnErrCode &= ~ERROR_PRAM_CRCCHK;
+		dev_dbg(pTAS2557->dev, "Block[0x%x] PChkSum match\n", pBlock->mnType);
+	}
+
+	if (pBlock->mbYChkSumPresent) {
+		if (nCRCChkSum != pBlock->mnYChkSum) {
+			dev_err(pTAS2557->dev, "Block YChkSum Error: FW = 0x%x, YCRC = 0x%x\n",
+				pBlock->mnYChkSum, nCRCChkSum);
+			nResult = -EAGAIN;
+			pTAS2557->mnErrCode |= ERROR_YRAM_CRCCHK;
+			goto check;
+		}
+		pTAS2557->mnErrCode &= ~ERROR_YRAM_CRCCHK;
+		nResult = 0;
+		dev_dbg(pTAS2557->dev, "Block[0x%x] YChkSum match\n", pBlock->mnType);
+	}
+
+check:
+	if (nResult == -EAGAIN) {
+		nRetry--;
+		if (nRetry > 0)
+			goto start;
+	}
+
+end:
+	if (nResult < 0) {
+		dev_err(pTAS2557->dev, "Block (%d) load error\n",
+				pBlock->mnType);
+	}
+	return nResult;
+}
+
+static int tas2557_load_data(struct tas2557_priv *pTAS2557, struct TData *pData, unsigned int nType)
+{
+	int nResult = 0;
+	unsigned int nBlock;
+	struct TBlock *pBlock;
+
+	dev_dbg(pTAS2557->dev,
+		"TAS2557 load data: %s, Blocks = %d, Block Type = %d\n", pData->mpName, pData->mnBlocks, nType);
+
+	for (nBlock = 0; nBlock < pData->mnBlocks; nBlock++) {
+		pBlock = &(pData->mpBlocks[nBlock]);
+		if (pBlock->mnType == nType) {
+			nResult = tas2557_load_block(pTAS2557, pBlock);
+			if (nResult < 0)
+				break;
+		}
+	}
+
+	return nResult;
+}
+
+static int tas2557_load_configuration(struct tas2557_priv *pTAS2557,
+	unsigned int nConfiguration, bool bLoadSame)
+{
+	int nResult = 0;
+	struct TConfiguration *pCurrentConfiguration = NULL;
+	struct TConfiguration *pNewConfiguration = NULL;
+
+	dev_dbg(pTAS2557->dev, "%s: %d\n", __func__, nConfiguration);
+
+	if ((!pTAS2557->mpFirmware->mpPrograms) ||
+		(!pTAS2557->mpFirmware->mpConfigurations)) {
+		dev_err(pTAS2557->dev, "Firmware not loaded\n");
+		nResult = 0;
+		goto end;
+	}
+
+	if (nConfiguration >= pTAS2557->mpFirmware->mnConfigurations) {
+		dev_err(pTAS2557->dev, "Configuration %d doesn't exist\n",
+			nConfiguration);
+		nResult = 0;
+		goto end;
+	}
+
+	if ((!pTAS2557->mbLoadConfigurationPrePowerUp)
+		&& (nConfiguration == pTAS2557->mnCurrentConfiguration)
+		&& (!bLoadSame)) {
+		dev_info(pTAS2557->dev, "Configuration %d is already loaded\n",
+			nConfiguration);
+		nResult = 0;
+		goto end;
+	}
+
+	pCurrentConfiguration =
+		&(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]);
+	pNewConfiguration =
+		&(pTAS2557->mpFirmware->mpConfigurations[nConfiguration]);
+	if (pNewConfiguration->mnProgram != pCurrentConfiguration->mnProgram) {
+		dev_err(pTAS2557->dev, "Configuration %d, %s doesn't share the same program as current %d\n",
+			nConfiguration, pNewConfiguration->mpName, pCurrentConfiguration->mnProgram);
+		nResult = 0;
+		goto end;
+	}
+
+	if (pNewConfiguration->mnPLL >= pTAS2557->mpFirmware->mnPLLs) {
+		dev_err(pTAS2557->dev, "Configuration %d, %s doesn't have a valid PLL index %d\n",
+			nConfiguration, pNewConfiguration->mpName, pNewConfiguration->mnPLL);
+		nResult = 0;
+		goto end;
+	}
+
+	if (pTAS2557->mbPowerUp) {
+		pTAS2557->mbLoadConfigurationPrePowerUp = false;
+		nResult = tas2557_load_coefficient(pTAS2557, pTAS2557->mnCurrentConfiguration, nConfiguration, true);
+	} else {
+		dev_dbg(pTAS2557->dev,
+			"TAS2557 was powered down, will load coefficient when power up\n");
+		pTAS2557->mbLoadConfigurationPrePowerUp = true;
+		pTAS2557->mnNewConfiguration = nConfiguration;
+	}
+
+end:
+
+	if (nResult < 0) {
+		if (pTAS2557->mnErrCode & (ERROR_DEVA_I2C_COMM | ERROR_PRAM_CRCCHK | ERROR_YRAM_CRCCHK))
+			failsafe(pTAS2557);
+	}
+
+	return nResult;
+}
+
+int tas2557_set_config(struct tas2557_priv *pTAS2557, int config)
+{
+	struct TConfiguration *pConfiguration;
+	struct TProgram *pProgram;
+	unsigned int nProgram = pTAS2557->mnCurrentProgram;
+	unsigned int nConfiguration = config;
+	int nResult = 0;
+
+	if ((!pTAS2557->mpFirmware->mpPrograms) ||
+		(!pTAS2557->mpFirmware->mpConfigurations)) {
+		dev_err(pTAS2557->dev, "Firmware not loaded\n");
+		nResult = -EINVAL;
+		goto end;
+	}
+
+	if (nConfiguration >= pTAS2557->mpFirmware->mnConfigurations) {
+		dev_err(pTAS2557->dev, "Configuration %d doesn't exist\n",
+			nConfiguration);
+		nResult = -EINVAL;
+		goto end;
+	}
+
+	pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[nConfiguration]);
+	pProgram = &(pTAS2557->mpFirmware->mpPrograms[nProgram]);
+
+	if (nProgram != pConfiguration->mnProgram) {
+		dev_err(pTAS2557->dev,
+			"Configuration %d, %s with Program %d isn't compatible with existing Program %d, %s\n",
+			nConfiguration, pConfiguration->mpName, pConfiguration->mnProgram,
+			nProgram, pProgram->mpName);
+		nResult = -EINVAL;
+		goto end;
+	}
+
+	nResult = tas2557_load_configuration(pTAS2557, nConfiguration, false);
+
+end:
+
+	return nResult;
+}
+
+void tas2557_clear_firmware(struct TFirmware *pFirmware)
+{
+	unsigned int n, nn;
+
+	if (!pFirmware)
+		return;
+
+	kfree(pFirmware->mpDescription);
+
+	if (pFirmware->mpPLLs != NULL) {
+		for (n = 0; n < pFirmware->mnPLLs; n++) {
+			kfree(pFirmware->mpPLLs[n].mpDescription);
+			kfree(pFirmware->mpPLLs[n].mBlock.mpData);
+		}
+		kfree(pFirmware->mpPLLs);
+	}
+
+	if (pFirmware->mpPrograms != NULL) {
+		for (n = 0; n < pFirmware->mnPrograms; n++) {
+			kfree(pFirmware->mpPrograms[n].mpDescription);
+			kfree(pFirmware->mpPrograms[n].mData.mpDescription);
+			for (nn = 0; nn < pFirmware->mpPrograms[n].mData.mnBlocks; nn++)
+				kfree(pFirmware->mpPrograms[n].mData.mpBlocks[nn].mpData);
+			kfree(pFirmware->mpPrograms[n].mData.mpBlocks);
+		}
+		kfree(pFirmware->mpPrograms);
+	}
+
+	if (pFirmware->mpConfigurations != NULL) {
+		for (n = 0; n < pFirmware->mnConfigurations; n++) {
+			kfree(pFirmware->mpConfigurations[n].mpDescription);
+			kfree(pFirmware->mpConfigurations[n].mData.mpDescription);
+			for (nn = 0; nn < pFirmware->mpConfigurations[n].mData.mnBlocks; nn++)
+				kfree(pFirmware->mpConfigurations[n].mData.mpBlocks[nn].mpData);
+			kfree(pFirmware->mpConfigurations[n].mData.mpBlocks);
+		}
+		kfree(pFirmware->mpConfigurations);
+	}
+
+	if (pFirmware->mpCalibrations != NULL) {
+		for (n = 0; n < pFirmware->mnCalibrations; n++) {
+			kfree(pFirmware->mpCalibrations[n].mpDescription);
+			kfree(pFirmware->mpCalibrations[n].mData.mpDescription);
+			for (nn = 0; nn < pFirmware->mpCalibrations[n].mData.mnBlocks; nn++)
+				kfree(pFirmware->mpCalibrations[n].mData.mpBlocks[nn].mpData);
+			kfree(pFirmware->mpCalibrations[n].mData.mpBlocks);
+		}
+		kfree(pFirmware->mpCalibrations);
+	}
+
+	memset(pFirmware, 0x00, sizeof(struct TFirmware));
+}
+
+static int tas2557_load_calibration(struct tas2557_priv *pTAS2557,	char *pFileName)
+{
+	int nResult = 0;
+
+	int nFile;
+	mm_segment_t fs;
+	unsigned char pBuffer[1000];
+	int nSize = 0;
+
+	dev_dbg(pTAS2557->dev, "%s:\n", __func__);
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	nFile = sys_open(pFileName, O_RDONLY, 0);
+
+	dev_info(pTAS2557->dev, "TAS2557 calibration file = %s, handle = %d\n",
+		pFileName, nFile);
+
+	if (nFile >= 0) {
+		nSize = sys_read(nFile, pBuffer, 1000);
+		sys_close(nFile);
+	} else {
+		dev_err(pTAS2557->dev, "TAS2557 cannot open calibration file: %s\n",
+			pFileName);
+	}
+
+	set_fs(fs);
+
+	if (!nSize)
+		goto end;
+
+	tas2557_clear_firmware(pTAS2557->mpCalFirmware);
+	dev_info(pTAS2557->dev, "TAS2557 calibration file size = %d\n", nSize);
+	nResult = fw_parse(pTAS2557, pTAS2557->mpCalFirmware, pBuffer, nSize);
+
+	if (nResult)
+		dev_err(pTAS2557->dev, "TAS2557 calibration file is corrupt\n");
+	else
+		dev_info(pTAS2557->dev, "TAS2557 calibration: %d calibrations\n",
+			pTAS2557->mpCalFirmware->mnCalibrations);
+end:
+
+	return nResult;
+}
+
+static bool tas2557_get_coefficient_in_block(struct tas2557_priv *pTAS2557,
+	struct TBlock *pBlock, int nReg, int *pnValue)
+{
+	int nCoefficient = 0;
+	bool bFound = false;
+	unsigned char *pCommands;
+	int nBook, nPage, nOffset, len;
+	int i, n;
+
+	pCommands = pBlock->mpData;
+	for (i = 0 ; i < pBlock->mnCommands;) {
+		nBook = pCommands[4 * i + 0];
+		nPage = pCommands[4 * i + 1];
+		nOffset = pCommands[4 * i + 2];
+		if ((nOffset < 0x7f) || (nOffset == 0x81))
+			i++;
+		else if (nOffset == 0x85) {
+			len = ((int)nBook << 8) | nPage;
+			nBook = pCommands[4 * i + 4];
+			nPage = pCommands[4 * i + 5];
+			nOffset = pCommands[4 * i + 6];
+			n = 4 * i + 7;
+			i += 2;
+			i += ((len - 1) / 4);
+			if ((len - 1) % 4)
+				i++;
+			if ((nBook != TAS2557_BOOK_ID(nReg))
+				|| (nPage != TAS2557_PAGE_ID(nReg)))
+				continue;
+			if (nOffset > TAS2557_PAGE_REG(nReg))
+				continue;
+			if ((len + nOffset) >= (TAS2557_PAGE_REG(nReg) + 4)) {
+				n += (TAS2557_PAGE_REG(nReg) - nOffset);
+				nCoefficient = ((int)pCommands[n] << 24)
+						| ((int)pCommands[n + 1] << 16)
+						| ((int)pCommands[n + 2] << 8)
+						| (int)pCommands[n + 3];
+				bFound = true;
+				break;
+			}
+		} else {
+			dev_err(pTAS2557->dev, "%s, format error %d\n", __func__, nOffset);
+			break;
+		}
+	}
+
+	if (bFound) {
+		*pnValue = nCoefficient;
+		dev_dbg(pTAS2557->dev, "%s, B[0x%x]P[0x%x]R[0x%x]=0x%x\n", __func__,
+			TAS2557_BOOK_ID(nReg), TAS2557_PAGE_ID(nReg), TAS2557_PAGE_REG(nReg),
+			nCoefficient);
+	}
+
+	return bFound;
+}
+
+static bool tas2557_get_coefficient_in_data(struct tas2557_priv *pTAS2557,
+	struct TData *pData, int blockType, int nReg, int *pnValue)
+{
+	bool bFound = false;
+	struct TBlock *pBlock;
+	int i;
+
+	for (i = 0; i < pData->mnBlocks; i++) {
+		pBlock = &(pData->mpBlocks[i]);
+		if (pBlock->mnType == blockType) {
+			bFound = tas2557_get_coefficient_in_block(pTAS2557,
+						pBlock, nReg, pnValue);
+			if (bFound)
+				break;
+		}
+	}
+
+	return bFound;
+}
+
+static bool tas2557_find_Tmax_in_configuration(struct tas2557_priv *pTAS2557,
+	struct TConfiguration *pConfiguration, int *pnTMax)
+{
+	struct TData *pData;
+	bool bFound = false;
+	int nBlockType, nReg, nCoefficient;
+
+	if (pTAS2557->mnPGID == TAS2557_PG_VERSION_2P1)
+		nReg = TAS2557_PG2P1_CALI_T_REG;
+	else
+		nReg = TAS2557_PG1P0_CALI_T_REG;
+
+	nBlockType = TAS2557_BLOCK_CFG_COEFF_DEV_A;
+
+	pData = &(pConfiguration->mData);
+	bFound = tas2557_get_coefficient_in_data(pTAS2557, pData, nBlockType, nReg, &nCoefficient);
+	if (bFound)
+		*pnTMax = nCoefficient;
+
+	return bFound;
+}
+
+void tas2557_fw_ready(const struct firmware *pFW, void *pContext)
+{
+	struct tas2557_priv *pTAS2557 = (struct tas2557_priv *) pContext;
+	int nResult;
+	unsigned int nProgram = 0;
+	unsigned int nSampleRate = 0;
+
+#ifdef CONFIG_TAS2557_CODEC
+	mutex_lock(&pTAS2557->codec_lock);
+#endif
+
+#ifdef CONFIG_TAS2557_MISC
+	mutex_lock(&pTAS2557->file_lock);
+#endif
+
+	printk("[tas2557] tas2557_fw_ready +++++ \n"); //[jinjia]Trace
+	dev_info(pTAS2557->dev, "%s:\n", __func__);
+
+	if (unlikely(!pFW) || unlikely(!pFW->data)) {
+		dev_err(pTAS2557->dev, "%s firmware is not loaded.\n",
+			TAS2557_FW_NAME);
+		goto end;
+	}
+
+	if (pTAS2557->mpFirmware->mpConfigurations) {
+		nProgram = pTAS2557->mnCurrentProgram;
+		nSampleRate = pTAS2557->mnCurrentSampleRate;
+		dev_dbg(pTAS2557->dev, "clear current firmware\n");
+		tas2557_clear_firmware(pTAS2557->mpFirmware);
+	}
+
+	nResult = fw_parse(pTAS2557, pTAS2557->mpFirmware, (unsigned char *)(pFW->data), pFW->size);
+	release_firmware(pFW);
+	if (nResult < 0) {
+		dev_err(pTAS2557->dev, "firmware is corrupt\n");
+		goto end;
+	}
+
+	if (!pTAS2557->mpFirmware->mnPrograms) {
+		dev_err(pTAS2557->dev, "firmware contains no programs\n");
+		nResult = -EINVAL;
+		goto end;
+	}
+
+	if (!pTAS2557->mpFirmware->mnConfigurations) {
+		dev_err(pTAS2557->dev, "firmware contains no configurations\n");
+		nResult = -EINVAL;
+		goto end;
+	}
+
+	if (nProgram >= pTAS2557->mpFirmware->mnPrograms) {
+		dev_info(pTAS2557->dev,
+			"no previous program, set to default\n");
+		nProgram = 0;
+	}
+
+	pTAS2557->mnCurrentSampleRate = nSampleRate;
+	nResult = tas2557_set_program(pTAS2557, nProgram, -1);
+
+	printk("[tas2557] tas2557_fw_ready ----- \n"); //[jinjia]Trace
+end:
+
+#ifdef CONFIG_TAS2557_CODEC
+	mutex_unlock(&pTAS2557->codec_lock);
+#endif
+
+#ifdef CONFIG_TAS2557_MISC
+	mutex_unlock(&pTAS2557->file_lock);
+#endif
+}
+
+int tas2557_set_program(struct tas2557_priv *pTAS2557,
+	unsigned int nProgram, int nConfig)
+{
+	struct TProgram *pProgram;
+	unsigned int nConfiguration = 0;
+	unsigned int nSampleRate = 0;
+	unsigned char nGain;
+	bool bFound = false;
+	int nResult = 0;
+
+	if ((!pTAS2557->mpFirmware->mpPrograms) ||
+		(!pTAS2557->mpFirmware->mpConfigurations)) {
+		dev_err(pTAS2557->dev, "Firmware not loaded\n");
+		nResult = 0;
+		goto end;
+	}
+
+	if (nProgram >= pTAS2557->mpFirmware->mnPrograms) {
+		dev_err(pTAS2557->dev, "TAS2557: Program %d doesn't exist\n",
+			nProgram);
+		nResult = 0;
+		goto end;
+	}
+
+	if (nConfig < 0) {
+		nConfiguration = 0;
+		nSampleRate = pTAS2557->mnCurrentSampleRate;
+		while (!bFound && (nConfiguration < pTAS2557->mpFirmware->mnConfigurations)) {
+			if (pTAS2557->mpFirmware->mpConfigurations[nConfiguration].mnProgram == nProgram) {
+				if (nSampleRate == 0) {
+					bFound = true;
+					dev_info(pTAS2557->dev, "find default configuration %d\n", nConfiguration);
+				} else if (nSampleRate == pTAS2557->mpFirmware->mpConfigurations[nConfiguration].mnSamplingRate) {
+					bFound = true;
+					dev_info(pTAS2557->dev, "find matching configuration %d\n", nConfiguration);
+				} else {
+					nConfiguration++;
+				}
+			} else {
+				nConfiguration++;
+			}
+		}
+		if (!bFound) {
+			dev_err(pTAS2557->dev,
+				"Program %d, no valid configuration found for sample rate %d, ignore\n",
+				nProgram, nSampleRate);
+			nResult = 0;
+			goto end;
+		}
+	} else {
+		if (pTAS2557->mpFirmware->mpConfigurations[nConfig].mnProgram != nProgram) {
+			dev_err(pTAS2557->dev, "%s, configuration program doesn't match\n", __func__);
+			nResult = 0;
+			goto end;
+		}
+		nConfiguration = nConfig;
+	}
+
+	pProgram = &(pTAS2557->mpFirmware->mpPrograms[nProgram]);
+	if (pTAS2557->mbPowerUp) {
+		dev_info(pTAS2557->dev,
+			"device powered up, power down to load program %d (%s)\n",
+			nProgram, pProgram->mpName);
+		if (hrtimer_active(&pTAS2557->mtimer))
+			hrtimer_cancel(&pTAS2557->mtimer);
+
+		if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE)
+			pTAS2557->enableIRQ(pTAS2557, false, false);
+
+		nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_data);
+		if (nResult < 0)
+			goto end;
+	}
+
+	pTAS2557->hw_reset(pTAS2557);
+	nResult = pTAS2557->write(pTAS2557, TAS2557_SW_RESET_REG, 0x01);
+	if (nResult < 0)
+		goto end;
+	msleep(1);
+	nResult = tas2557_load_default(pTAS2557);
+	if (nResult < 0)
+		goto end;
+
+	dev_info(pTAS2557->dev, "load program %d (%s)\n", nProgram, pProgram->mpName);
+	nResult = tas2557_load_data(pTAS2557, &(pProgram->mData), TAS2557_BLOCK_PGM_DEV_A);
+	if (nResult < 0)
+		goto end;
+	pTAS2557->mnCurrentProgram = nProgram;
+
+	nResult = tas2557_get_DAC_gain(pTAS2557, &nGain);
+	if (nResult < 0)
+		goto end;
+	pTAS2557->mnDevGain = nGain;
+	pTAS2557->mnDevCurrentGain = nGain;
+
+	nResult = tas2557_load_coefficient(pTAS2557, -1, nConfiguration, false);
+	if (nResult < 0)
+		goto end;
+
+	tas2557_update_edge(pTAS2557);
+
+	if (pTAS2557->mbPowerUp) {
+		pTAS2557->clearIRQ(pTAS2557);
+		dev_dbg(pTAS2557->dev, "device powered up, load startup\n");
+		nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_startup_data);
+		if (nResult < 0)
+			goto end;
+		if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) {
+			nResult = tas2557_checkPLL(pTAS2557);
+			if (nResult < 0) {
+				nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_data);
+				pTAS2557->mbPowerUp = false;
+				goto end;
+			}
+		}
+		dev_dbg(pTAS2557->dev, "device powered up, load unmute\n");
+		nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_unmute_data);
+		if (nResult < 0)
+			goto end;
+
+		if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) {
+			pTAS2557->enableIRQ(pTAS2557, true, true);
+			if (!hrtimer_active(&pTAS2557->mtimer)) {
+				pTAS2557->mnDieTvReadCounter = 0;
+				hrtimer_start(&pTAS2557->mtimer,
+					ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+			}
+		}
+	}
+
+end:
+
+	if (nResult < 0) {
+		if (pTAS2557->mnErrCode & (ERROR_DEVA_I2C_COMM | ERROR_PRAM_CRCCHK | ERROR_YRAM_CRCCHK))
+			failsafe(pTAS2557);
+	}
+	return nResult;
+}
+
+int tas2557_set_calibration(struct tas2557_priv *pTAS2557, int nCalibration)
+{
+	struct TCalibration *pCalibration = NULL;
+	struct TConfiguration *pConfiguration;
+	struct TProgram *pProgram;
+	int nTmax = 0;
+	bool bFound = false;
+	int nResult = 0;
+
+	if ((!pTAS2557->mpFirmware->mpPrograms)
+		|| (!pTAS2557->mpFirmware->mpConfigurations)) {
+		dev_err(pTAS2557->dev, "Firmware not loaded\n\r");
+		nResult = 0;
+		goto end;
+	}
+
+	if (nCalibration == 0x00FF) {
+		nResult = tas2557_load_calibration(pTAS2557, TAS2557_CAL_NAME);
+		if (nResult < 0) {
+			dev_info(pTAS2557->dev, "load new calibration file %s fail %d\n",
+				TAS2557_CAL_NAME, nResult);
+			goto end;
+		}
+		nCalibration = 0;
+	}
+
+	if (nCalibration >= pTAS2557->mpCalFirmware->mnCalibrations) {
+		dev_err(pTAS2557->dev,
+			"Calibration %d doesn't exist\n", nCalibration);
+		nResult = 0;
+		goto end;
+	}
+
+	pTAS2557->mnCurrentCalibration = nCalibration;
+	if (pTAS2557->mbLoadConfigurationPrePowerUp)
+		goto end;
+
+	pCalibration = &(pTAS2557->mpCalFirmware->mpCalibrations[nCalibration]);
+	pProgram = &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]);
+	pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]);
+	if (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE) {
+		if (pTAS2557->mbBypassTMax) {
+			bFound = tas2557_find_Tmax_in_configuration(pTAS2557, pConfiguration, &nTmax);
+			if (bFound && (nTmax == TAS2557_COEFFICIENT_TMAX)) {
+				dev_dbg(pTAS2557->dev, "%s, config[%s] bypass load calibration\n",
+					__func__, pConfiguration->mpName);
+				goto end;
+			}
+		}
+
+		dev_dbg(pTAS2557->dev, "%s, load calibration\n", __func__);
+		nResult = tas2557_load_data(pTAS2557, &(pCalibration->mData), TAS2557_BLOCK_CFG_COEFF_DEV_A);
+		if (nResult < 0)
+			goto end;
+	}
+
+end:
+	if (nResult < 0) {
+		tas2557_clear_firmware(pTAS2557->mpCalFirmware);
+		nResult = tas2557_set_program(pTAS2557, pTAS2557->mnCurrentProgram, pTAS2557->mnCurrentConfiguration);
+	}
+
+	return nResult;
+}
+
+bool tas2557_get_Cali_prm_r0(struct tas2557_priv *pTAS2557, int *prm_r0)
+{
+	struct TCalibration *pCalibration;
+	struct TData *pData;
+	int nReg;
+	int nCali_Re;
+	bool bFound = false;
+	int nBlockType;
+
+	if (!pTAS2557->mpCalFirmware->mnCalibrations) {
+		dev_err(pTAS2557->dev, "%s, no calibration data\n", __func__);
+		goto end;
+	}
+
+	if (pTAS2557->mnPGID == TAS2557_PG_VERSION_2P1)
+		nReg = TAS2557_PG2P1_CALI_R0_REG;
+	else
+		nReg = TAS2557_PG1P0_CALI_R0_REG;
+
+	nBlockType = TAS2557_BLOCK_CFG_COEFF_DEV_A;
+
+	pCalibration = &(pTAS2557->mpCalFirmware->mpCalibrations[pTAS2557->mnCurrentCalibration]);
+	pData = &(pCalibration->mData);
+
+	bFound = tas2557_get_coefficient_in_data(pTAS2557, pData, nBlockType, nReg, &nCali_Re);
+
+end:
+
+	if (bFound)
+		*prm_r0 = nCali_Re;
+
+	return bFound;
+}
+
+int tas2557_parse_dt(struct device *dev, struct tas2557_priv *pTAS2557)
+{
+	struct device_node *np = dev->of_node;
+	int rc = 0, ret = 0;
+	unsigned int value;
+
+	pTAS2557->mnResetGPIO = of_get_named_gpio(np, "ti,cdc-reset-gpio", 0);
+	if (!gpio_is_valid(pTAS2557->mnResetGPIO)) {
+		dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n",
+			"ti,cdc-reset-gpio", np->full_name,
+			pTAS2557->mnResetGPIO);
+		ret = -EINVAL;
+		goto end;
+	} else
+		dev_dbg(pTAS2557->dev, "ti,cdc-reset-gpio=%d\n", pTAS2557->mnResetGPIO);
+
+	pTAS2557->mnGpioINT = of_get_named_gpio(np, "ti,irq-gpio", 0);
+	if (!gpio_is_valid(pTAS2557->mnGpioINT))
+		dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n",
+			"ti,irq-gpio", np->full_name,
+			pTAS2557->mnGpioINT);
+
+
+	rc = of_property_read_u32(np, "ti,i2s-bits", &value);
+	if (rc)
+		dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n",
+			"ti,i2s-bits", np->full_name, rc);
+	else
+		pTAS2557->mnI2SBits = value;
+
+	rc = of_property_read_u32(np, "ti,bypass-tmax", &value);
+	if (rc)
+		dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n",
+			"ti,bypass-tmax", np->full_name, rc);
+	else
+		pTAS2557->mbBypassTMax = (value > 0);
+
+end:
+
+	return ret;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2557 common functions for Android Linux");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tas2557/tas2557-core.h b/sound/soc/codecs/tas2557/tas2557-core.h
new file mode 100644
index 0000000..cb46eee
--- /dev/null
+++ b/sound/soc/codecs/tas2557/tas2557-core.h
@@ -0,0 +1,79 @@
+/*
+** =============================================================================
+** 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:
+**     tas2557-core.h
+**
+** Description:
+**     header file for tas2557-core.c
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2557_CORE_H
+#define _TAS2557_CORE_H
+
+#include "tas2557.h"
+
+#define TAS2557_YRAM_BOOK1				140
+
+#define TAS2557_YRAM1_PAGE				42
+#define TAS2557_YRAM1_START_REG			88
+#define TAS2557_YRAM1_END_REG			127
+
+#define TAS2557_YRAM2_START_PAGE		43
+#define TAS2557_YRAM2_END_PAGE			49
+#define TAS2557_YRAM2_START_REG			8
+#define TAS2557_YRAM2_END_REG			127
+
+#define TAS2557_YRAM3_PAGE				50
+#define TAS2557_YRAM3_START_REG			8
+#define TAS2557_YRAM3_END_REG			27
+
+/* should not include B0_P53_R44-R47 */
+#define TAS2557_YRAM_BOOK2				0
+#define TAS2557_YRAM4_START_PAGE		50
+#define TAS2557_YRAM4_END_PAGE			60
+#define TAS2557_YRAM4_START_REG			8
+#define TAS2557_YRAM4_END_REG			127
+
+#define TAS2557_YRAM5_PAGE				61
+#define TAS2557_YRAM5_START_REG			8
+#define TAS2557_YRAM5_END_REG			27
+
+#define TAS2557_COEFFICIENT_TMAX	0x7fffffff
+#define TAS2557_SAFE_GUARD_PATTERN		0x5a
+#define LOW_TEMPERATURE_CHECK_PERIOD 5000	/* 5 second */
+
+struct TYCRC {
+	unsigned char mnOffset;
+	unsigned char mnLen;
+};
+
+int tas2557_enable(struct tas2557_priv *pTAS2557, bool bEnable);
+int tas2557_SA_DevChnSetup(struct tas2557_priv *pTAS2557, unsigned int mode);
+int tas2557_get_die_temperature(struct tas2557_priv *pTAS2557, int *pTemperature);
+int tas2557_set_sampling_rate(struct tas2557_priv *pTAS2557, unsigned int nSamplingRate);
+int tas2557_set_bit_rate(struct tas2557_priv *pTAS2557, unsigned int nBitRate);
+int tas2557_get_bit_rate(struct tas2557_priv *pTAS2557, unsigned char *pBitRate);
+int tas2557_set_config(struct tas2557_priv *pTAS2557, int config);
+void tas2557_fw_ready(const struct firmware *pFW, void *pContext);
+bool tas2557_get_Cali_prm_r0(struct tas2557_priv *pTAS2557, int *prm_r0);
+int tas2557_set_program(struct tas2557_priv *pTAS2557, unsigned int nProgram, int nConfig);
+int tas2557_set_calibration(struct tas2557_priv *pTAS2557, int nCalibration);
+int tas2557_load_default(struct tas2557_priv *pTAS2557);
+int tas2557_parse_dt(struct device *dev, struct tas2557_priv *pTAS2557);
+int tas2557_get_DAC_gain(struct tas2557_priv *pTAS2557, unsigned char *pnGain);
+int tas2557_set_DAC_gain(struct tas2557_priv *pTAS2557, unsigned int nGain);
+int tas2557_configIRQ(struct tas2557_priv *pTAS2557);
+int tas2557_update_edge(struct tas2557_priv *pTAS2557);
+#endif /* _TAS2557_CORE_H */
diff --git a/sound/soc/codecs/tas2557/tas2557-misc.c b/sound/soc/codecs/tas2557/tas2557-misc.c
new file mode 100644
index 0000000..925a322
--- /dev/null
+++ b/sound/soc/codecs/tas2557/tas2557-misc.c
@@ -0,0 +1,597 @@
+/*
+** =============================================================================
+** 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:
+**     tas2557-misc.c
+**
+** Description:
+**     misc driver for Texas Instruments TAS2557 High Performance 4W Smart Amplifier
+**
+** =============================================================================
+*/
+
+#ifdef CONFIG_TAS2557_MISC
+
+#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 "tas2557.h"
+#include "tas2557-core.h"
+#include "tas2557-misc.h"
+#include <linux/dma-mapping.h>
+
+static int g_logEnable = 1;
+static struct tas2557_priv *g_tas2557;
+
+static int tas2557_file_open(struct inode *inode, struct file *file)
+{
+	struct tas2557_priv *pTAS2557 = g_tas2557;
+
+	if (!try_module_get(THIS_MODULE))
+		return -ENODEV;
+
+	file->private_data = (void *)pTAS2557;
+	if (g_logEnable)
+		dev_info(pTAS2557->dev,	"%s\n", __func__);
+	return 0;
+}
+
+static int tas2557_file_release(struct inode *inode, struct file *file)
+{
+	struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)file->private_data;
+
+	if (g_logEnable)
+		dev_info(pTAS2557->dev,	"%s\n", __func__);
+	file->private_data = (void *)NULL;
+	module_put(THIS_MODULE);
+
+	return 0;
+}
+
+static ssize_t tas2557_file_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)file->private_data;
+	int ret = 0;
+	unsigned int nValue = 0;
+	unsigned char value = 0;
+	unsigned char *p_kBuf = NULL;
+
+	mutex_lock(&pTAS2557->file_lock);
+
+	switch (pTAS2557->mnDBGCmd) {
+	case TIAUDIO_CMD_REG_READ: {
+		if (g_logEnable)
+			dev_info(pTAS2557->dev, "TIAUDIO_CMD_REG_READ: current_reg = 0x%x, count=%d\n",
+				pTAS2557->mnCurrentReg, (int)count);
+		if (count == 1) {
+			ret = pTAS2557->read(pTAS2557, pTAS2557->mnCurrentReg, &nValue);
+			if (ret < 0)
+				break;
+
+			value = (u8)nValue;
+			if (g_logEnable)
+				dev_info(pTAS2557->dev, "TIAUDIO_CMD_REG_READ: nValue=0x%x, value=0x%x\n", nValue, value);
+			ret = copy_to_user(buf, &value, 1);
+			if (ret != 0) {
+				/* Failed to copy all the data, exit */
+				dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+			}
+		} else if (count > 1) {
+			p_kBuf = kzalloc(count, GFP_KERNEL);
+			if (p_kBuf != NULL) {
+				ret = pTAS2557->bulk_read(pTAS2557, pTAS2557->mnCurrentReg, p_kBuf, count);
+				if (ret < 0)
+					break;
+				ret = copy_to_user(buf, p_kBuf, count);
+				if (ret != 0) {
+					/* Failed to copy all the data, exit */
+					dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+				}
+				kfree(p_kBuf);
+			} else
+				dev_err(pTAS2557->dev, "read no mem\n");
+		}
+	}
+	break;
+
+	case TIAUDIO_CMD_PROGRAM: {
+		if ((pTAS2557->mpFirmware->mnConfigurations > 0)
+			&& (pTAS2557->mpFirmware->mnPrograms > 0)) {
+			if (g_logEnable)
+				dev_info(pTAS2557->dev,	"TIAUDIO_CMD_PROGRAM: count = %d\n", (int)count);
+
+			if (count == PROGRAM_BUF_SIZE) {
+				p_kBuf = kzalloc(count, GFP_KERNEL);
+				if (p_kBuf != NULL) {
+					struct TProgram *pProgram =
+						&(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]);
+					p_kBuf[0] = pTAS2557->mpFirmware->mnPrograms;
+					p_kBuf[1] = pTAS2557->mnCurrentProgram;
+					p_kBuf[2] = pProgram->mnAppMode;
+					p_kBuf[3] = (pProgram->mnBoost&0xff00)>>8;
+					p_kBuf[4] = (pProgram->mnBoost&0x00ff);
+					memcpy(&p_kBuf[5], pProgram->mpName, FW_NAME_SIZE);
+					strlcpy(&p_kBuf[5+FW_NAME_SIZE], pProgram->mpDescription, strlen(pProgram->mpDescription) + 1);
+					ret = copy_to_user(buf, p_kBuf, count);
+					if (ret != 0) {
+						/* Failed to copy all the data, exit */
+						dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+					}
+					kfree(p_kBuf);
+				} else
+					dev_err(pTAS2557->dev, "read no mem\n");
+			} else
+				dev_err(pTAS2557->dev, "read buffer not sufficient\n");
+		} else
+			dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__);
+	}
+	break;
+
+	case TIAUDIO_CMD_CONFIGURATION: {
+		if ((pTAS2557->mpFirmware->mnConfigurations > 0)
+		&& (pTAS2557->mpFirmware->mnPrograms > 0)) {
+			if (g_logEnable)
+				dev_info(pTAS2557->dev, "TIAUDIO_CMD_CONFIGURATION: count = %d\n", (int)count);
+			if (count == CONFIGURATION_BUF_SIZE) {
+				p_kBuf = kzalloc(count, GFP_KERNEL);
+				if (p_kBuf != NULL) {
+					struct TConfiguration *pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]);
+
+					p_kBuf[0] = pTAS2557->mpFirmware->mnConfigurations;
+					p_kBuf[1] = pTAS2557->mnCurrentConfiguration;
+					memcpy(&p_kBuf[2], pConfiguration->mpName, FW_NAME_SIZE);
+					p_kBuf[2+FW_NAME_SIZE] = pConfiguration->mnProgram;
+					p_kBuf[3+FW_NAME_SIZE] = pConfiguration->mnPLL;
+					p_kBuf[4+FW_NAME_SIZE] = (pConfiguration->mnSamplingRate&0x000000ff);
+					p_kBuf[5+FW_NAME_SIZE] = ((pConfiguration->mnSamplingRate&0x0000ff00)>>8);
+					p_kBuf[6+FW_NAME_SIZE] = ((pConfiguration->mnSamplingRate&0x00ff0000)>>16);
+					p_kBuf[7+FW_NAME_SIZE] = ((pConfiguration->mnSamplingRate&0xff000000)>>24);
+					strlcpy(&p_kBuf[8+FW_NAME_SIZE], pConfiguration->mpDescription, strlen(pConfiguration->mpDescription)+1);
+					ret = copy_to_user(buf, p_kBuf, count);
+					if (ret != 0) {
+						/* Failed to copy all the data, exit */
+						dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+					}
+					kfree(p_kBuf);
+				} else
+					dev_err(pTAS2557->dev, "read no mem\n");
+			} else
+				dev_err(pTAS2557->dev, "read buffer not sufficient\n");
+		} else
+			dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__);
+	}
+	break;
+
+	case TIAUDIO_CMD_FW_TIMESTAMP: {
+		if (g_logEnable)
+			dev_info(pTAS2557->dev, "TIAUDIO_CMD_FW_TIMESTAMP: count = %d\n", (int)count);
+
+		if (count == 4) {
+			p_kBuf = kzalloc(count, GFP_KERNEL);
+			if (p_kBuf != NULL) {
+				p_kBuf[0] = (pTAS2557->mpFirmware->mnTimeStamp&0x000000ff);
+				p_kBuf[1] = ((pTAS2557->mpFirmware->mnTimeStamp&0x0000ff00)>>8);
+				p_kBuf[2] = ((pTAS2557->mpFirmware->mnTimeStamp&0x00ff0000)>>16);
+				p_kBuf[3] = ((pTAS2557->mpFirmware->mnTimeStamp&0xff000000)>>24);
+				ret = copy_to_user(buf, p_kBuf, count);
+				if (ret != 0) {
+					/* Failed to copy all the data, exit */
+					dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+				}
+				kfree(p_kBuf);
+			} else
+				dev_err(pTAS2557->dev, "read no mem\n");
+		}
+	}
+	break;
+
+	case TIAUDIO_CMD_CALIBRATION: {
+		if (g_logEnable)
+			dev_info(pTAS2557->dev, "TIAUDIO_CMD_CALIBRATION: count = %d\n", (int)count);
+
+		if (count == 1) {
+			unsigned char curCal = pTAS2557->mnCurrentCalibration;
+
+			ret = copy_to_user(buf, &curCal, 1);
+			if (ret != 0) {
+				/* Failed to copy all the data, exit */
+				dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+			}
+		}
+	}
+	break;
+
+	case TIAUDIO_CMD_SAMPLERATE: {
+		if (g_logEnable)
+			dev_info(pTAS2557->dev, "TIAUDIO_CMD_SAMPLERATE: count = %d\n", (int)count);
+		if (count == 4) {
+			p_kBuf = kzalloc(count, GFP_KERNEL);
+			if (p_kBuf != NULL) {
+				struct TConfiguration *pConfiguration =
+					&(pTAS2557->mpFirmware->mpConfigurations[pTAS2557->mnCurrentConfiguration]);
+
+				p_kBuf[0] = (pConfiguration->mnSamplingRate&0x000000ff);
+				p_kBuf[1] = ((pConfiguration->mnSamplingRate&0x0000ff00)>>8);
+				p_kBuf[2] = ((pConfiguration->mnSamplingRate&0x00ff0000)>>16);
+				p_kBuf[3] = ((pConfiguration->mnSamplingRate&0xff000000)>>24);
+
+				ret = copy_to_user(buf, p_kBuf, count);
+				if (ret != 0) {
+					/* Failed to copy all the data, exit */
+					dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+				}
+
+				kfree(p_kBuf);
+			} else
+				dev_err(pTAS2557->dev, "read no mem\n");
+		}
+	}
+	break;
+
+	case TIAUDIO_CMD_BITRATE: {
+		if (g_logEnable)
+			dev_info(pTAS2557->dev,
+					"TIAUDIO_CMD_BITRATE: count = %d\n", (int)count);
+
+		if (count == 1) {
+			unsigned char bitRate = 0;
+			ret = tas2557_get_bit_rate(pTAS2557, &bitRate);
+			if (ret >= 0) {
+				ret = copy_to_user(buf, &bitRate, 1);
+				if (ret != 0) {
+					/* Failed to copy all the data, exit */
+					dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+				}
+			}
+		}
+	}
+	break;
+
+	case TIAUDIO_CMD_DACVOLUME: {
+		if (g_logEnable)
+			dev_info(pTAS2557->dev, "TIAUDIO_CMD_DACVOLUME: count = %d\n", (int)count);
+
+		if (count == 1) {
+			unsigned char volume = 0;
+
+			ret = tas2557_get_DAC_gain(pTAS2557, &volume);
+			if (ret >= 0) {
+				ret = copy_to_user(buf, &volume, 1);
+				if (ret != 0) {
+				/* Failed to copy all the data, exit */
+					dev_err(pTAS2557->dev, "copy to user fail %d\n", ret);
+				}
+			}
+		}
+	}
+	break;
+	}
+	pTAS2557->mnDBGCmd = 0;
+
+	mutex_unlock(&pTAS2557->file_lock);
+	return count;
+}
+
+static ssize_t tas2557_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+	struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)file->private_data;
+	int ret = 0;
+	unsigned char *p_kBuf = NULL;
+	unsigned int reg = 0;
+	unsigned int len = 0;
+
+	mutex_lock(&pTAS2557->file_lock);
+
+	p_kBuf = kzalloc(count, GFP_KERNEL);
+	if (p_kBuf == NULL) {
+		dev_err(pTAS2557->dev, "write no mem\n");
+		goto err;
+	}
+
+	ret = copy_from_user(p_kBuf, buf, count);
+	if (ret != 0) {
+		dev_err(pTAS2557->dev, "copy_from_user failed.\n");
+		goto err;
+	}
+
+	pTAS2557->mnDBGCmd = p_kBuf[0];
+	switch (pTAS2557->mnDBGCmd) {
+	case TIAUDIO_CMD_REG_WITE:
+		if (count > 5) {
+			reg = ((unsigned int)p_kBuf[1] << 24)
+				+ ((unsigned int)p_kBuf[2] << 16)
+				+ ((unsigned int)p_kBuf[3] << 8)
+				+ (unsigned int)p_kBuf[4];
+			len = count - 5;
+			if (len == 1) {
+				ret = pTAS2557->write(pTAS2557, reg, p_kBuf[5]);
+				if (g_logEnable)
+					dev_info(pTAS2557->dev, "TIAUDIO_CMD_REG_WITE, Reg=0x%x, Val=0x%x\n", reg, p_kBuf[5]);
+			} else
+				ret = pTAS2557->bulk_write(pTAS2557, reg, &p_kBuf[5], len);
+		} else
+			dev_err(pTAS2557->dev, "%s, write len fail, count=%d.\n", __func__, (int)count);
+		pTAS2557->mnDBGCmd = 0;
+	break;
+
+	case TIAUDIO_CMD_REG_READ:
+		if (count == 5) {
+			pTAS2557->mnCurrentReg = ((unsigned int)p_kBuf[1] << 24)
+				+ ((unsigned int)p_kBuf[2] << 16)
+				+ ((unsigned int)p_kBuf[3] << 8)
+				+ (unsigned int)p_kBuf[4];
+			if (g_logEnable)
+				dev_info(pTAS2557->dev, "TIAUDIO_CMD_REG_READ whole=0x%x\n", pTAS2557->mnCurrentReg);
+		} else
+			dev_err(pTAS2557->dev, "read len fail.\n");
+	break;
+
+	case TIAUDIO_CMD_DEBUG_ON:
+		if (count == 2)
+			g_logEnable = p_kBuf[1];
+
+		pTAS2557->mnDBGCmd = 0;
+	break;
+
+	case TIAUDIO_CMD_PROGRAM:
+	{
+		if (count == 2) {
+			if ((pTAS2557->mpFirmware->mnConfigurations > 0)
+				&& (pTAS2557->mpFirmware->mnPrograms > 0)) {
+				int config = -1;
+
+				if (p_kBuf[1] == pTAS2557->mnCurrentProgram)
+					config = pTAS2557->mnCurrentConfiguration;
+				if (g_logEnable)
+					dev_info(pTAS2557->dev, "TIAUDIO_CMD_PROGRAM, set to %d, cfg=%d\n", p_kBuf[1], config);
+				tas2557_set_program(pTAS2557, p_kBuf[1], config);
+				pTAS2557->mnDBGCmd = 0;
+			} else
+				dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__);
+		}
+	}
+	break;
+
+	case TIAUDIO_CMD_CONFIGURATION:
+	{
+		if (count == 2) {
+			if ((pTAS2557->mpFirmware->mnConfigurations > 0)
+			&& (pTAS2557->mpFirmware->mnPrograms > 0)) {
+				if (g_logEnable)
+					dev_info(pTAS2557->dev, "TIAUDIO_CMD_CONFIGURATION, set to %d\n", p_kBuf[1]);
+				tas2557_set_config(pTAS2557, p_kBuf[1]);
+				pTAS2557->mnDBGCmd = 0;
+			} else
+				dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__);
+		}
+	}
+	break;
+
+	case TIAUDIO_CMD_FW_TIMESTAMP:
+	/*let go*/
+	break;
+
+	case TIAUDIO_CMD_CALIBRATION:
+	{
+		if (count == 2) {
+			if ((pTAS2557->mpFirmware->mnConfigurations > 0)
+			&& (pTAS2557->mpFirmware->mnPrograms > 0)) {
+				if (g_logEnable)
+					dev_info(pTAS2557->dev, "TIAUDIO_CMD_CALIBRATION, set to %d\n", p_kBuf[1]);
+				tas2557_set_calibration(pTAS2557, p_kBuf[1]);
+				pTAS2557->mnDBGCmd = 0;
+			}
+		}
+	}
+	break;
+
+	case TIAUDIO_CMD_SAMPLERATE:
+		if (count == 5) {
+			unsigned int nSampleRate = ((unsigned int)p_kBuf[1] << 24) +
+				((unsigned int)p_kBuf[2] << 16) +
+				((unsigned int)p_kBuf[3] << 8) +
+				(unsigned int)p_kBuf[4];
+			if (g_logEnable)
+				dev_info(pTAS2557->dev, "TIAUDIO_CMD_SAMPLERATE, set to %d\n", nSampleRate);
+
+			tas2557_set_sampling_rate(pTAS2557, nSampleRate);
+		}
+	break;
+
+	case TIAUDIO_CMD_BITRATE:
+		if (count == 2) {
+			if (g_logEnable)
+				dev_info(pTAS2557->dev, "TIAUDIO_CMD_BITRATE, set to %d\n", p_kBuf[1]);
+
+			tas2557_set_bit_rate(pTAS2557, p_kBuf[1]);
+		}
+	break;
+
+	case TIAUDIO_CMD_DACVOLUME:
+		if (count == 2) {
+			unsigned char volume;
+
+			volume = (p_kBuf[1] & 0x0f);
+			if (g_logEnable)
+				dev_info(pTAS2557->dev, "TIAUDIO_CMD_DACVOLUME, set to %d\n", volume);
+
+			ret = tas2557_set_DAC_gain(pTAS2557, volume);
+			if (ret < 0)
+				goto err;
+		}
+	break;
+
+	case TIAUDIO_CMD_SPEAKER:
+		if (count == 2) {
+			if (g_logEnable)
+				dev_info(pTAS2557->dev, "TIAUDIO_CMD_SPEAKER, set to %d\n", p_kBuf[1]);
+			tas2557_enable(pTAS2557, (p_kBuf[1] > 0));
+		}
+	break;
+
+	case TIAUDIO_CMD_FW_RELOAD:
+		if (count == 1) {
+			const char *pFWName;
+			if (pTAS2557->mnPGID == TAS2557_PG_VERSION_2P1)
+				pFWName = TAS2557_FW_NAME;
+			else if (pTAS2557->mnPGID == TAS2557_PG_VERSION_1P0)
+				pFWName = TAS2557_PG1P0_FW_NAME;
+			else
+				break;
+
+			ret = request_firmware_nowait(THIS_MODULE, 1, pFWName,
+				pTAS2557->dev, GFP_KERNEL, pTAS2557, tas2557_fw_ready);
+
+			if (g_logEnable)
+				dev_info(pTAS2557->dev, "TIAUDIO_CMD_FW_RELOAD: ret = %d\n", ret);
+		}
+	break;
+
+	default:
+		pTAS2557->mnDBGCmd = 0;
+	break;
+	}
+
+err:
+	if (p_kBuf != NULL)
+		kfree(p_kBuf);
+
+	mutex_unlock(&pTAS2557->file_lock);
+
+	return count;
+}
+
+static long tas2557_file_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct tas2557_priv *pTAS2557 = file->private_data;
+	int ret = 0;
+
+	mutex_lock(&pTAS2557->file_lock);
+
+	switch (cmd) {
+	case SMARTPA_SPK_DAC_VOLUME:
+	{
+	}
+	break;
+
+	case SMARTPA_SPK_POWER_ON:
+	{
+	    printk("[tas2557] SMARTPA_SPK_POWER_ON \n"); //[jinjia]Trace
+		tas2557_enable(pTAS2557, true);
+	}
+	break;
+
+	case SMARTPA_SPK_POWER_OFF:
+	{
+	    printk("[tas2557] SMARTPA_SPK_POWER_OFF \n"); //[jinjia]Trace
+		tas2557_enable(pTAS2557, false);
+	}
+	break;
+
+	case SMARTPA_SPK_SWITCH_PROGRAM:
+	{
+		if ((pTAS2557->mpFirmware->mnConfigurations > 0)
+			&& (pTAS2557->mpFirmware->mnPrograms > 0))
+			tas2557_set_program(pTAS2557, arg, -1);
+	}
+	break;
+
+	case SMARTPA_SPK_SWITCH_CONFIGURATION:
+	{
+		if ((pTAS2557->mpFirmware->mnConfigurations > 0)
+			&& (pTAS2557->mpFirmware->mnPrograms > 0))
+			tas2557_set_config(pTAS2557, arg);
+	}
+	break;
+
+	case SMARTPA_SPK_SWITCH_CALIBRATION:
+	{
+		if ((pTAS2557->mpFirmware->mnConfigurations > 0)
+			&& (pTAS2557->mpFirmware->mnPrograms > 0))
+			tas2557_set_calibration(pTAS2557, arg);
+	}
+	break;
+
+	case SMARTPA_SPK_SET_SAMPLERATE:
+	{
+		tas2557_set_sampling_rate(pTAS2557, arg);
+	}
+	break;
+
+	case SMARTPA_SPK_SET_BITRATE:
+	{
+		tas2557_set_bit_rate(pTAS2557, arg);
+	}
+	break;
+	}
+
+	mutex_unlock(&pTAS2557->file_lock);
+	return ret;
+}
+
+static const struct file_operations fops = {
+	.owner = THIS_MODULE,
+	.read = tas2557_file_read,
+	.write = tas2557_file_write,
+	.unlocked_ioctl = tas2557_file_unlocked_ioctl,
+	.open = tas2557_file_open,
+	.release = tas2557_file_release,
+};
+
+#define MODULE_NAME	"tas2557"
+static struct miscdevice tas2557_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = MODULE_NAME,
+	.fops = &fops,
+};
+
+int tas2557_register_misc(struct tas2557_priv *pTAS2557)
+{
+	int ret = 0;
+
+	g_tas2557 = pTAS2557;
+
+	ret = misc_register(&tas2557_misc);
+	if (ret)
+		dev_err(pTAS2557->dev, "TAS2557 misc fail: %d\n", ret);
+
+	dev_info(pTAS2557->dev, "%s, leave\n", __func__);
+
+	return ret;
+}
+
+int tas2557_deregister_misc(struct tas2557_priv *pTAS2557)
+{
+	misc_deregister(&tas2557_misc);
+	return 0;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2557 Misc Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
+#endif
diff --git a/sound/soc/codecs/tas2557/tas2557-misc.h b/sound/soc/codecs/tas2557/tas2557-misc.h
new file mode 100644
index 0000000..38bfa64
--- /dev/null
+++ b/sound/soc/codecs/tas2557/tas2557-misc.h
@@ -0,0 +1,57 @@
+/*
+** =============================================================================
+** 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:
+**     tas2557-misc.h
+**
+** Description:
+**     header file for tas2557-misc.c
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2557_MISC_H
+#define _TAS2557_MISC_H
+
+#define	FW_NAME_SIZE			64
+#define	FW_DESCRIPTION_SIZE		256
+#define	PROGRAM_BUF_SIZE		(5 + FW_NAME_SIZE + FW_DESCRIPTION_SIZE)
+#define	CONFIGURATION_BUF_SIZE	(8 + FW_NAME_SIZE + FW_DESCRIPTION_SIZE)
+
+#define	TIAUDIO_CMD_REG_WITE			1
+#define	TIAUDIO_CMD_REG_READ			2
+#define	TIAUDIO_CMD_DEBUG_ON			3
+#define	TIAUDIO_CMD_PROGRAM				4
+#define	TIAUDIO_CMD_CONFIGURATION		5
+#define	TIAUDIO_CMD_FW_TIMESTAMP		6
+#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
+#define	TIAUDIO_CMD_FW_RELOAD			12
+
+#define	TAS2557_MAGIC_NUMBER	0x3537	/* '2557' */
+
+#define	SMARTPA_SPK_DAC_VOLUME				_IOWR(TAS2557_MAGIC_NUMBER, 1, unsigned long)
+#define	SMARTPA_SPK_POWER_ON				_IOWR(TAS2557_MAGIC_NUMBER, 2, unsigned long)
+#define	SMARTPA_SPK_POWER_OFF				_IOWR(TAS2557_MAGIC_NUMBER, 3, unsigned long)
+#define	SMARTPA_SPK_SWITCH_PROGRAM			_IOWR(TAS2557_MAGIC_NUMBER, 4, unsigned long)
+#define	SMARTPA_SPK_SWITCH_CONFIGURATION	_IOWR(TAS2557_MAGIC_NUMBER, 5, unsigned long)
+#define	SMARTPA_SPK_SWITCH_CALIBRATION		_IOWR(TAS2557_MAGIC_NUMBER, 6, unsigned long)
+#define	SMARTPA_SPK_SET_SAMPLERATE			_IOWR(TAS2557_MAGIC_NUMBER, 7, unsigned long)
+#define	SMARTPA_SPK_SET_BITRATE				_IOWR(TAS2557_MAGIC_NUMBER, 8, unsigned long)
+
+int tas2557_register_misc(struct tas2557_priv *pTAS2557);
+int tas2557_deregister_misc(struct tas2557_priv *pTAS2557);
+
+#endif /* _TAS2557_MISC_H */
diff --git a/sound/soc/codecs/tas2557/tas2557-regmap.c b/sound/soc/codecs/tas2557/tas2557-regmap.c
new file mode 100644
index 0000000..cd260af
--- /dev/null
+++ b/sound/soc/codecs/tas2557/tas2557-regmap.c
@@ -0,0 +1,917 @@
+/*
+** =============================================================================
+** 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:
+**     tas2557-regmap.c
+**
+** Description:
+**     I2C driver with regmap for Texas Instruments TAS2557 High Performance 4W Smart Amplifier
+**
+** =============================================================================
+*/
+
+#ifdef CONFIG_TAS2557_REGMAP
+
+#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/uaccess.h>
+//[FairPhone][Audio][jinjia]=2020.05.19=Fix the build error with TI sample code. -s
+#include <linux/interrupt.h>
+//[FairPhone][Audio][jinjia]=2020.05.19=Fix the build error with TI sample code. -e
+#include "tas2557.h"
+#include "tas2557-core.h"
+
+#ifdef CONFIG_TAS2557_CODEC
+#include "tas2557-codec.h"
+#endif
+
+#ifdef CONFIG_TAS2557_MISC
+#include "tas2557-misc.h"
+#endif
+
+#define ENABLE_TILOAD
+#ifdef ENABLE_TILOAD
+#include "tiload.h"
+#endif
+
+#define LOW_TEMPERATURE_GAIN 6
+#define LOW_TEMPERATURE_COUNTER 12
+
+static int tas2557_change_book_page(
+	struct tas2557_priv *pTAS2557,
+	unsigned char nBook,
+	unsigned char nPage)
+{
+	int nResult = 0;
+
+	if ((pTAS2557->mnCurrentBook == nBook)
+		&& pTAS2557->mnCurrentPage == nPage)
+		goto end;
+
+	if (pTAS2557->mnCurrentBook != nBook) {
+		nResult = regmap_write(pTAS2557->mpRegmap, TAS2557_BOOKCTL_PAGE, 0);
+		if (nResult < 0) {
+			dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n",
+				__func__, __LINE__, nResult);
+			goto end;
+		}
+		pTAS2557->mnCurrentPage = 0;
+		nResult = regmap_write(pTAS2557->mpRegmap, TAS2557_BOOKCTL_REG, nBook);
+		if (nResult < 0) {
+			dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n",
+				__func__, __LINE__, nResult);
+			goto end;
+		}
+		pTAS2557->mnCurrentBook = nBook;
+		if (nPage != 0) {
+			nResult = regmap_write(pTAS2557->mpRegmap, TAS2557_BOOKCTL_PAGE, nPage);
+			if (nResult < 0) {
+				dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n",
+					__func__, __LINE__, nResult);
+				goto end;
+			}
+			pTAS2557->mnCurrentPage = nPage;
+		}
+	} else if (pTAS2557->mnCurrentPage != nPage) {
+		nResult = regmap_write(pTAS2557->mpRegmap, TAS2557_BOOKCTL_PAGE, nPage);
+		if (nResult < 0) {
+			dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n",
+				__func__, __LINE__, nResult);
+			goto end;
+		}
+		pTAS2557->mnCurrentPage = nPage;
+	}
+
+end:
+	if (nResult < 0)
+		pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM;
+	else
+		pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM;
+
+	return nResult;
+}
+
+static int tas2557_dev_read(
+	struct tas2557_priv *pTAS2557,
+	unsigned int nRegister,
+	unsigned int *pValue)
+{
+	int nResult = 0;
+	unsigned int Value = 0;
+
+	mutex_lock(&pTAS2557->dev_lock);
+      printk("[tas2557] tas2557_dev_read +++++\n"); //[jinjia]Trace
+	if (pTAS2557->mbTILoadActive) {
+		if (!(nRegister & 0x80000000))
+			goto end; /* let only reads from TILoad pass. */
+		nRegister &= ~0x80000000;
+		printk("[tas2557] [tas2557_dev_read] mbTILoadActive \n"); //[jinjia]Trace
+		dev_dbg(pTAS2557->dev, "TiLoad R REG B[%d]P[%d]R[%d]\n",
+				TAS2557_BOOK_ID(nRegister),
+				TAS2557_PAGE_ID(nRegister),
+				TAS2557_PAGE_REG(nRegister));
+	}
+
+	nResult = tas2557_change_book_page(pTAS2557,
+				TAS2557_BOOK_ID(nRegister),
+				TAS2557_PAGE_ID(nRegister));
+	if (nResult >= 0) {
+		nResult = regmap_read(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister), &Value);
+		if (nResult < 0) {
+			dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n",
+				__func__, __LINE__, nResult);
+			pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM;
+			goto end;
+		} else
+			pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM;
+		*pValue = Value;
+	}
+      printk("[tas2557] tas2557_dev_read -----\n"); //[jinjia]Trace
+end:
+
+	mutex_unlock(&pTAS2557->dev_lock);
+	return nResult;
+}
+
+static int tas2557_dev_write(
+	struct tas2557_priv *pTAS2557,
+	unsigned int nRegister,
+	unsigned int nValue)
+{
+	int nResult = 0;
+
+	mutex_lock(&pTAS2557->dev_lock);
+	if ((nRegister == 0xAFFEAFFE) && (nValue == 0xBABEBABE)) {
+		pTAS2557->mbTILoadActive = true;
+		goto end;
+	}
+
+	if ((nRegister == 0xBABEBABE) && (nValue == 0xAFFEAFFE)) {
+		pTAS2557->mbTILoadActive = false;
+		goto end;
+	}
+
+	if (pTAS2557->mbTILoadActive) {
+		if (!(nRegister & 0x80000000))
+			goto end;/* let only writes from TILoad pass. */
+		nRegister &= ~0x80000000;
+
+		dev_dbg(pTAS2557->dev, "TiLoad W REG B[%d]P[%d]R[%d] =0x%x\n",
+						TAS2557_BOOK_ID(nRegister),
+						TAS2557_PAGE_ID(nRegister),
+						TAS2557_PAGE_REG(nRegister),
+						nValue);
+	}
+
+	nResult = tas2557_change_book_page(pTAS2557,
+				TAS2557_BOOK_ID(nRegister),
+				TAS2557_PAGE_ID(nRegister));
+	if (nResult >= 0) {
+		nResult = regmap_write(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister), nValue);
+		if (nResult < 0) {
+			dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n",
+				__func__, __LINE__, nResult);
+			pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM;
+		} else
+			pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM;
+	}
+
+end:
+
+	mutex_unlock(&pTAS2557->dev_lock);
+
+	return nResult;
+}
+
+static int tas2557_dev_bulk_read(
+	struct tas2557_priv *pTAS2557,
+	unsigned int nRegister,
+	u8 *pData,
+	unsigned int nLength)
+{
+	int nResult = 0;
+
+	mutex_lock(&pTAS2557->dev_lock);
+	if (pTAS2557->mbTILoadActive) {
+		if (!(nRegister & 0x80000000))
+			goto end; /* let only writes from TILoad pass. */
+
+		nRegister &= ~0x80000000;
+		dev_dbg(pTAS2557->dev, "TiLoad BR REG B[%d]P[%d]R[%d], count=%d\n",
+				TAS2557_BOOK_ID(nRegister),
+				TAS2557_PAGE_ID(nRegister),
+				TAS2557_PAGE_REG(nRegister),
+				nLength);
+	}
+
+	nResult = tas2557_change_book_page(pTAS2557,
+				TAS2557_BOOK_ID(nRegister),
+				TAS2557_PAGE_ID(nRegister));
+	if (nResult >= 0) {
+		nResult = regmap_bulk_read(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister), pData, nLength);
+		if (nResult < 0) {
+			dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n",
+				__func__, __LINE__, nResult);
+			pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM;
+		} else
+			pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM;
+	}
+
+end:
+
+	mutex_unlock(&pTAS2557->dev_lock);
+	return nResult;
+}
+
+static int tas2557_dev_bulk_write(
+	struct tas2557_priv *pTAS2557,
+	unsigned int nRegister,
+	u8 *pData,
+	unsigned int nLength)
+{
+	int nResult = 0;
+
+	mutex_lock(&pTAS2557->dev_lock);
+	if (pTAS2557->mbTILoadActive) {
+		if (!(nRegister & 0x80000000))
+			goto end; /* let only writes from TILoad pass. */
+
+		nRegister &= ~0x80000000;
+
+		dev_dbg(pTAS2557->dev, "TiLoad BW REG B[%d]P[%d]R[%d], count=%d\n",
+				TAS2557_BOOK_ID(nRegister),
+				TAS2557_PAGE_ID(nRegister),
+				TAS2557_PAGE_REG(nRegister),
+				nLength);
+	}
+
+	nResult = tas2557_change_book_page( pTAS2557,
+				TAS2557_BOOK_ID(nRegister),
+				TAS2557_PAGE_ID(nRegister));
+	if (nResult >= 0) {
+		nResult = regmap_bulk_write(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister), pData, nLength);
+		if (nResult < 0) {
+			dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n",
+				__func__, __LINE__, nResult);
+			pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM;
+		} else
+			pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM;
+	}
+
+end:
+
+	mutex_unlock(&pTAS2557->dev_lock);
+	return nResult;
+}
+
+static int tas2557_dev_update_bits(
+	struct tas2557_priv *pTAS2557,
+	unsigned int nRegister,
+	unsigned int nMask,
+	unsigned int nValue)
+{
+	int nResult = 0;
+
+	mutex_lock(&pTAS2557->dev_lock);
+
+	if (pTAS2557->mbTILoadActive) {
+		if (!(nRegister & 0x80000000))
+			goto end; /* let only writes from TILoad pass. */
+
+		nRegister &= ~0x80000000;
+		dev_dbg(pTAS2557->dev, "TiLoad SB REG B[%d]P[%d]R[%d], mask=0x%x, value=0x%x\n",
+				TAS2557_BOOK_ID(nRegister),
+				TAS2557_PAGE_ID(nRegister),
+				TAS2557_PAGE_REG(nRegister),
+				nMask, nValue);
+	}
+
+	nResult = tas2557_change_book_page( pTAS2557,
+				TAS2557_BOOK_ID(nRegister),
+				TAS2557_PAGE_ID(nRegister));
+	if (nResult >= 0) {
+		nResult = regmap_update_bits(pTAS2557->mpRegmap, TAS2557_PAGE_REG(nRegister), nMask, nValue);
+		if (nResult < 0) {
+			dev_err(pTAS2557->dev, "%s, %d, I2C error %d\n",
+				__func__, __LINE__, nResult);
+			pTAS2557->mnErrCode |= ERROR_DEVA_I2C_COMM;
+		} else
+			pTAS2557->mnErrCode &= ~ERROR_DEVA_I2C_COMM;
+	}
+
+end:
+	mutex_unlock(&pTAS2557->dev_lock);
+	return nResult;
+}
+
+void tas2557_clearIRQ(struct tas2557_priv *pTAS2557)
+{
+	unsigned int nValue;
+	int nResult = 0;
+
+	nResult = pTAS2557->read(pTAS2557, TAS2557_FLAGS_1, &nValue);
+	if (nResult >= 0)
+		pTAS2557->read(pTAS2557, TAS2557_FLAGS_2, &nValue);
+
+}
+
+
+void tas2557_enableIRQ(struct tas2557_priv *pTAS2557, bool enable, bool startup_chk)
+{
+	if (enable) {
+		if (!pTAS2557->mbIRQEnable) {
+			if (gpio_is_valid(pTAS2557->mnGpioINT)) {
+				enable_irq(pTAS2557->mnIRQ);
+				if (startup_chk) {
+					/* check after 10 ms */
+					schedule_delayed_work(&pTAS2557->irq_work, msecs_to_jiffies(10));
+				}
+				pTAS2557->mbIRQEnable = true;
+			}
+		}
+	} else {
+		if (gpio_is_valid(pTAS2557->mnGpioINT))
+			disable_irq_nosync(pTAS2557->mnIRQ);
+		pTAS2557->mbIRQEnable = false;
+	}
+}
+
+static void tas2557_hw_reset(struct tas2557_priv *pTAS2557)
+{
+    printk("[tas2557] tas2557_hw_reset ++++++\n"); //[jinjia]Trace
+	if (gpio_is_valid(pTAS2557->mnResetGPIO)) {
+    printk("[tas2557][tas2557_hw_reset] gpio_is_valid(mnResetGPIO) \n"); //[jinjia]Trace
+		gpio_direction_output(pTAS2557->mnResetGPIO, 0);
+		msleep(5);
+		gpio_direction_output(pTAS2557->mnResetGPIO, 1);
+		msleep(2);
+	}
+
+	pTAS2557->mnCurrentBook = -1;
+	pTAS2557->mnCurrentPage = -1;
+	if (pTAS2557->mnErrCode)
+		dev_info(pTAS2557->dev, "before reset, ErrCode=0x%x\n", pTAS2557->mnErrCode);
+	pTAS2557->mnErrCode = 0;
+    printk("[tas2557] tas2557_hw_reset ------\n"); //[jinjia]Trace
+}
+
+static void irq_work_routine(struct work_struct *work)
+{
+	int nResult = 0;
+	unsigned int nDevInt1Status = 0, nDevInt2Status = 0;
+	unsigned int nDevPowerUpFlag = 0;
+	int nCounter = 2;
+	struct tas2557_priv *pTAS2557 =
+		container_of(work, struct tas2557_priv, irq_work.work);
+
+#ifdef CONFIG_TAS2557_CODEC
+	mutex_lock(&pTAS2557->codec_lock);
+#endif
+
+#ifdef CONFIG_TAS2557_MISC
+	mutex_lock(&pTAS2557->file_lock);
+#endif
+
+	if(pTAS2557->mnErrCode & ERROR_FAILSAFE)
+		goto program;
+
+	if (pTAS2557->mbRuntimeSuspend) {
+		dev_info(pTAS2557->dev, "%s, Runtime Suspended\n", __func__);
+		goto end;
+	}
+
+	if (!pTAS2557->mbPowerUp) {
+		dev_info(pTAS2557->dev, "%s, device not powered\n", __func__);
+		goto end;
+	}
+
+	if ((!pTAS2557->mpFirmware->mnConfigurations)
+		|| (!pTAS2557->mpFirmware->mnPrograms)) {
+		dev_info(pTAS2557->dev, "%s, firmware not loaded\n", __func__);
+		goto end;
+	}
+	nResult = tas2557_dev_write(pTAS2557, TAS2557_GPIO4_PIN_REG, 0x00);
+	if (nResult < 0)
+		goto program;
+	nResult = tas2557_dev_read(pTAS2557, TAS2557_FLAGS_1, &nDevInt1Status);
+	if (nResult >= 0)
+		nResult = tas2557_dev_read(pTAS2557, TAS2557_FLAGS_2, &nDevInt2Status);
+	if (nResult < 0)
+		goto program;
+
+	if (((nDevInt1Status & 0xfc) != 0) || ((nDevInt2Status & 0x0c) != 0)) {
+		/* in case of INT_OC, INT_UV, INT_OT, INT_BO, INT_CL, INT_CLK1, INT_CLK2 */
+		dev_err(pTAS2557->dev, "critical error: 0x%x, 0x%x\n", nDevInt1Status, nDevInt2Status);
+		if (nDevInt1Status & 0x80) {
+			pTAS2557->mnErrCode |= ERROR_OVER_CURRENT;
+			dev_err(pTAS2557->dev, "DEVA SPK over current!\n");
+		} else
+			pTAS2557->mnErrCode &= ~ERROR_OVER_CURRENT;
+
+		if (nDevInt1Status & 0x40) {
+			pTAS2557->mnErrCode |= ERROR_UNDER_VOLTAGE;
+			dev_err(pTAS2557->dev, "DEVA SPK under voltage!\n");
+		} else
+			pTAS2557->mnErrCode &= ~ERROR_UNDER_VOLTAGE;
+
+		if (nDevInt1Status & 0x20) {
+			pTAS2557->mnErrCode |= ERROR_CLK_HALT;
+			dev_err(pTAS2557->dev, "DEVA clk halted!\n");
+		} else
+			pTAS2557->mnErrCode &= ~ERROR_CLK_HALT;
+
+		if (nDevInt1Status & 0x10) {
+			pTAS2557->mnErrCode |= ERROR_DIE_OVERTEMP;
+			dev_err(pTAS2557->dev, "DEVA die over temperature!\n");
+		} else
+			pTAS2557->mnErrCode &= ~ERROR_DIE_OVERTEMP;
+
+		if (nDevInt1Status & 0x08) {
+			pTAS2557->mnErrCode |= ERROR_BROWNOUT;
+			dev_err(pTAS2557->dev, "DEVA brownout!\n");
+		} else
+			pTAS2557->mnErrCode &= ~ERROR_BROWNOUT;
+
+		if (nDevInt1Status & 0x04) {
+			pTAS2557->mnErrCode |= ERROR_CLK_LOST;
+			dev_err(pTAS2557->dev, "DEVA clock lost!\n");
+		} else
+			pTAS2557->mnErrCode &= ~ERROR_CLK_LOST;
+
+		if (nDevInt2Status & 0x08) {
+			pTAS2557->mnErrCode |= ERROR_CLK_DET1;
+			dev_err(pTAS2557->dev, "DEVA clk detection 1!\n");
+		} else
+			pTAS2557->mnErrCode &= ~ERROR_CLK_DET1;
+
+		if (nDevInt2Status & 0x04) {
+			pTAS2557->mnErrCode |= ERROR_CLK_DET2;
+			dev_err(pTAS2557->dev, "DEVA clk detection 2!\n");
+		} else
+			pTAS2557->mnErrCode &= ~ERROR_CLK_DET2;
+
+		goto program;
+	} else {
+		dev_dbg(pTAS2557->dev, "IRQ Status: 0x%x, 0x%x\n", nDevInt1Status, nDevInt2Status);
+		nCounter = 2;
+		while (nCounter > 0) {
+			nResult = tas2557_dev_read(pTAS2557, TAS2557_POWER_UP_FLAG_REG, &nDevPowerUpFlag);
+			if (nResult < 0)
+				goto program;
+			if ((nDevPowerUpFlag & 0xc0) == 0xc0)
+				break;
+			nCounter--;
+			if (nCounter > 0) {
+				/* in case check pow status just after power on TAS2557 */
+				dev_dbg(pTAS2557->dev, "PowSts: 0x%x, check again after 10ms\n",
+					nDevPowerUpFlag);
+				msleep(10);
+			}
+		}
+		if ((nDevPowerUpFlag & 0xc0) != 0xc0) {
+			dev_err(pTAS2557->dev, "%s, Critical ERROR B[%d]_P[%d]_R[%d]= 0x%x\n",
+				__func__,
+				TAS2557_BOOK_ID(TAS2557_POWER_UP_FLAG_REG),
+				TAS2557_PAGE_ID(TAS2557_POWER_UP_FLAG_REG),
+				TAS2557_PAGE_REG(TAS2557_POWER_UP_FLAG_REG),
+				nDevPowerUpFlag);
+			pTAS2557->mnErrCode |= ERROR_CLASSD_PWR;
+			goto program;
+		}
+		pTAS2557->mnErrCode &= ~ERROR_CLASSD_PWR;
+
+		dev_dbg(pTAS2557->dev, "%s: INT1=0x%x, INT2=0x%x; PowerUpFlag=0x%x\n",
+			__func__, nDevInt1Status, nDevInt2Status, nDevPowerUpFlag);
+		goto end;
+	}
+
+program:
+	/* hardware reset and reload */
+	nResult = -1;
+	tas2557_set_program(pTAS2557, pTAS2557->mnCurrentProgram, pTAS2557->mnCurrentConfiguration);
+
+end:
+	if (nResult >= 0) {
+		tas2557_dev_write(pTAS2557, TAS2557_GPIO4_PIN_REG, 0x07);
+		tas2557_enableIRQ(pTAS2557, true, false);
+	}
+#ifdef CONFIG_TAS2557_MISC
+	mutex_unlock(&pTAS2557->file_lock);
+#endif
+
+#ifdef CONFIG_TAS2557_CODEC
+	mutex_unlock(&pTAS2557->codec_lock);
+#endif
+}
+
+static irqreturn_t tas2557_irq_handler(int irq, void *dev_id)
+{
+	struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)dev_id;
+
+	tas2557_enableIRQ(pTAS2557, false, false);
+	/* get IRQ status after 100 ms */
+	schedule_delayed_work(&pTAS2557->irq_work, msecs_to_jiffies(100));
+	return IRQ_HANDLED;
+}
+
+static enum hrtimer_restart temperature_timer_func(struct hrtimer *timer)
+{
+	struct tas2557_priv *pTAS2557 = container_of(timer, struct tas2557_priv, mtimer);
+
+	if (pTAS2557->mbPowerUp) {
+		schedule_work(&pTAS2557->mtimerwork);
+		if (gpio_is_valid(pTAS2557->mnGpioINT)) {
+			tas2557_enableIRQ(pTAS2557, false, false);
+			schedule_delayed_work(&pTAS2557->irq_work, msecs_to_jiffies(1));
+		}
+	}
+	return HRTIMER_NORESTART;
+}
+
+static void timer_work_routine(struct work_struct *work)
+{
+	struct tas2557_priv *pTAS2557 = container_of(work, struct tas2557_priv, mtimerwork);
+	int nResult, nActTemp;
+	int nTemp = 0;
+	struct TProgram *pProgram;
+	static int nAvg;
+
+#ifdef CONFIG_TAS2557_CODEC
+	mutex_lock(&pTAS2557->codec_lock);
+#endif
+
+#ifdef CONFIG_TAS2557_MISC
+	mutex_lock(&pTAS2557->file_lock);
+#endif
+
+	if (pTAS2557->mbRuntimeSuspend) {
+		dev_info(pTAS2557->dev, "%s, Runtime Suspended\n", __func__);
+		goto end;
+	}
+
+	if (!pTAS2557->mpFirmware->mnConfigurations) {
+		dev_info(pTAS2557->dev, "%s, firmware not loaded\n", __func__);
+		goto end;
+	}
+
+	pProgram = &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]);
+	if (!pTAS2557->mbPowerUp
+		|| (pProgram->mnAppMode != TAS2557_APP_TUNINGMODE)) {
+		dev_info(pTAS2557->dev, "%s, pass, Pow=%d, program=%s\n",
+			__func__, pTAS2557->mbPowerUp, pProgram->mpName);
+		goto end;
+	}
+
+	nResult = tas2557_get_die_temperature(pTAS2557, &nTemp);
+	if (nResult >= 0) {
+		nActTemp = (int)(nTemp >> 23);
+		dev_dbg(pTAS2557->dev, "Die=0x%x, degree=%d\n", nTemp, nActTemp);
+		if (!pTAS2557->mnDieTvReadCounter)
+			nAvg = 0;
+		pTAS2557->mnDieTvReadCounter++;
+		nAvg += nActTemp;
+		if (!(pTAS2557->mnDieTvReadCounter % LOW_TEMPERATURE_COUNTER)) {
+			nAvg /= LOW_TEMPERATURE_COUNTER;
+			dev_dbg(pTAS2557->dev, "check : avg=%d\n", nAvg);
+			if (nAvg < -6) {
+				/* if Die temperature is below -6 degree C */
+				if (pTAS2557->mnDevCurrentGain != LOW_TEMPERATURE_GAIN) {
+					nResult = tas2557_set_DAC_gain(pTAS2557, LOW_TEMPERATURE_GAIN);
+					if (nResult < 0)
+						goto end;
+					pTAS2557->mnDevCurrentGain = LOW_TEMPERATURE_GAIN;
+					dev_dbg(pTAS2557->dev, "LOW Temp: set gain to %d\n", LOW_TEMPERATURE_GAIN);
+				}
+			} else if (nAvg > 5) {
+				/* if Die temperature is above 5 degree C */
+				if (pTAS2557->mnDevCurrentGain != pTAS2557->mnDevGain) {
+					nResult = tas2557_set_DAC_gain(pTAS2557, pTAS2557->mnDevGain);
+					if (nResult < 0)
+						goto end;
+					pTAS2557->mnDevCurrentGain = pTAS2557->mnDevGain;
+					dev_dbg(pTAS2557->dev, "LOW Temp: set gain to original\n");
+				}
+			}
+			nAvg = 0;
+		}
+
+		if (pTAS2557->mbPowerUp)
+			hrtimer_start(&pTAS2557->mtimer,
+				ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+	}
+
+end:
+
+#ifdef CONFIG_TAS2557_MISC
+	mutex_unlock(&pTAS2557->file_lock);
+#endif
+
+#ifdef CONFIG_TAS2557_CODEC
+	mutex_unlock(&pTAS2557->codec_lock);
+#endif
+}
+
+static int tas2557_runtime_suspend(struct tas2557_priv *pTAS2557)
+{
+	dev_dbg(pTAS2557->dev, "%s\n", __func__);
+
+	pTAS2557->mbRuntimeSuspend = true;
+
+	if (hrtimer_active(&pTAS2557->mtimer)) {
+		dev_dbg(pTAS2557->dev, "cancel die temp timer\n");
+		hrtimer_cancel(&pTAS2557->mtimer);
+	}
+	if (work_pending(&pTAS2557->mtimerwork)) {
+		dev_dbg(pTAS2557->dev, "cancel timer work\n");
+		cancel_work_sync(&pTAS2557->mtimerwork);
+	}
+	if (gpio_is_valid(pTAS2557->mnGpioINT)) {
+		if (delayed_work_pending(&pTAS2557->irq_work)) {
+			dev_dbg(pTAS2557->dev, "cancel IRQ work\n");
+			cancel_delayed_work_sync(&pTAS2557->irq_work);
+		}
+	}
+
+	return 0;
+}
+
+static int tas2557_runtime_resume(struct tas2557_priv *pTAS2557)
+{
+	struct TProgram *pProgram;
+
+	dev_dbg(pTAS2557->dev, "%s\n", __func__);
+	if (!pTAS2557->mpFirmware->mpPrograms) {
+		dev_dbg(pTAS2557->dev, "%s, firmware not loaded\n", __func__);
+		goto end;
+	}
+
+	if (pTAS2557->mnCurrentProgram >= pTAS2557->mpFirmware->mnPrograms) {
+		dev_err(pTAS2557->dev, "%s, firmware corrupted\n", __func__);
+		goto end;
+	}
+
+	pProgram = &(pTAS2557->mpFirmware->mpPrograms[pTAS2557->mnCurrentProgram]);
+	if (pTAS2557->mbPowerUp && (pProgram->mnAppMode == TAS2557_APP_TUNINGMODE)) {
+		if (!hrtimer_active(&pTAS2557->mtimer)) {
+			dev_dbg(pTAS2557->dev, "%s, start Die Temp check timer\n", __func__);
+			pTAS2557->mnDieTvReadCounter = 0;
+			hrtimer_start(&pTAS2557->mtimer,
+				ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+		}
+	}
+
+	pTAS2557->mbRuntimeSuspend = false;
+end:
+
+	return 0;
+}
+
+static bool tas2557_volatile(struct device *pDev, unsigned int nRegister)
+{
+	return true;
+}
+
+static bool tas2557_writeable(struct device *pDev, unsigned int nRegister)
+{
+	return true;
+}
+
+static const struct regmap_config tas2557_i2c_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.writeable_reg = tas2557_writeable,
+	.volatile_reg = tas2557_volatile,
+	.cache_type = REGCACHE_NONE,
+	.max_register = 128,
+};
+
+/* tas2557_i2c_probe :
+* platform dependent
+* should implement hardware reset functionality
+*/
+static int tas2557_i2c_probe(struct i2c_client *pClient,
+	const struct i2c_device_id *pID)
+{
+	struct tas2557_priv *pTAS2557;
+	int nResult = 0;
+	unsigned int nValue = 0;
+	const char *pFWName;
+
+    printk("[tas2557] tas2557_i2c_probe +++++\n"); //[jinjia]Trace
+	dev_info(&pClient->dev, "%s enter\n", __func__);
+
+	pTAS2557 = devm_kzalloc(&pClient->dev, sizeof(struct tas2557_priv), GFP_KERNEL);
+	if (!pTAS2557) {
+		nResult = -ENOMEM;
+		goto err;
+	}
+
+	pTAS2557->dev = &pClient->dev;
+	i2c_set_clientdata(pClient, pTAS2557);
+	dev_set_drvdata(&pClient->dev, pTAS2557);
+
+	pTAS2557->mpRegmap = devm_regmap_init_i2c(pClient, &tas2557_i2c_regmap);
+	if (IS_ERR(pTAS2557->mpRegmap)) {
+		nResult = PTR_ERR(pTAS2557->mpRegmap);
+		dev_err(&pClient->dev, "Failed to allocate register map: %d\n",
+			nResult);
+		goto err;
+	}
+
+	if (pClient->dev.of_node)
+		tas2557_parse_dt(&pClient->dev, pTAS2557);
+
+	if (gpio_is_valid(pTAS2557->mnResetGPIO)) {
+		printk("[tas2557] gpio_is_valid (mnResetGPIO) +++++ \n "); //[jinjia]Trace
+		nResult = gpio_request(pTAS2557->mnResetGPIO, "TAS2557-RESET");
+		if (nResult < 0) {
+			dev_err(pTAS2557->dev, "%s: GPIO %d request error\n",
+				__func__, pTAS2557->mnResetGPIO);
+			goto err;
+		}
+		tas2557_hw_reset(pTAS2557);
+	}
+
+	pTAS2557->read = tas2557_dev_read;
+	pTAS2557->write = tas2557_dev_write;
+	pTAS2557->bulk_read = tas2557_dev_bulk_read;
+	pTAS2557->bulk_write = tas2557_dev_bulk_write;
+	pTAS2557->update_bits = tas2557_dev_update_bits;
+	pTAS2557->enableIRQ = tas2557_enableIRQ;
+	pTAS2557->clearIRQ = tas2557_clearIRQ;
+	pTAS2557->set_config = tas2557_set_config;
+	pTAS2557->set_calibration = tas2557_set_calibration;
+	pTAS2557->hw_reset = tas2557_hw_reset;
+	pTAS2557->runtime_suspend = tas2557_runtime_suspend;
+	pTAS2557->runtime_resume = tas2557_runtime_resume;
+	pTAS2557->mnRestart = 0;
+	pTAS2557->mnEdge = 4;
+
+	mutex_init(&pTAS2557->dev_lock);
+
+	/* Reset the chip */
+	nResult = tas2557_dev_write(pTAS2557, TAS2557_SW_RESET_REG, 0x01);
+	if (nResult < 0) {
+		dev_err(&pClient->dev, "I2c fail, %d\n", nResult);
+		goto err;
+	}
+
+	msleep(1);
+	tas2557_dev_read(pTAS2557, TAS2557_REV_PGID_REG, &nValue);
+	pTAS2557->mnPGID = nValue;
+	if (pTAS2557->mnPGID == TAS2557_PG_VERSION_2P1) {
+		dev_info(pTAS2557->dev, "PG2.1 Silicon found\n");
+		pFWName = TAS2557_FW_NAME;
+	} else if (pTAS2557->mnPGID == TAS2557_PG_VERSION_1P0) {
+		dev_info(pTAS2557->dev, "PG1.0 Silicon found\n");
+		pFWName = TAS2557_PG1P0_FW_NAME;
+	} else {
+		nResult = -ENOTSUPP;
+		dev_info(pTAS2557->dev, "unsupport Silicon 0x%x\n", pTAS2557->mnPGID);
+		goto err;
+	}
+
+	printk("[tas2557] gpio_is_valid(mnGpioINT) +\n"); //[jinjia]Trace
+	if (gpio_is_valid(pTAS2557->mnGpioINT)) {
+		printk("[tas2557] gpio_is_valid(mnGpioINT) ++\n"); //[jinjia]Trace
+		nResult = gpio_request(pTAS2557->mnGpioINT, "TAS2557-IRQ");
+		if (nResult < 0) {
+			dev_err(pTAS2557->dev,
+				"%s: GPIO %d request INT error\n",
+				__func__, pTAS2557->mnGpioINT);
+			goto err;
+		}
+
+		gpio_direction_input(pTAS2557->mnGpioINT);
+		pTAS2557->mnIRQ = gpio_to_irq(pTAS2557->mnGpioINT);
+		dev_dbg(pTAS2557->dev, "irq = %d\n", pTAS2557->mnIRQ);
+		INIT_DELAYED_WORK(&pTAS2557->irq_work, irq_work_routine);
+		nResult = request_threaded_irq(pTAS2557->mnIRQ, tas2557_irq_handler,
+					NULL, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				pClient->name, pTAS2557);
+		if (nResult < 0) {
+			dev_err(pTAS2557->dev,
+				"request_irq failed, %d\n", nResult);
+			goto err;
+		}
+		disable_irq_nosync(pTAS2557->mnIRQ);
+	}
+
+	pTAS2557->mpFirmware = devm_kzalloc(&pClient->dev, sizeof(struct TFirmware), GFP_KERNEL);
+	if (!pTAS2557->mpFirmware) {
+		nResult = -ENOMEM;
+		goto err;
+	}
+
+	pTAS2557->mpCalFirmware = devm_kzalloc(&pClient->dev, sizeof(struct TFirmware), GFP_KERNEL);
+	if (!pTAS2557->mpCalFirmware) {
+		nResult = -ENOMEM;
+		goto err;
+	}
+
+#ifdef CONFIG_TAS2557_CODEC
+	mutex_init(&pTAS2557->codec_lock);
+	tas2557_register_codec(pTAS2557);
+#endif
+
+#ifdef CONFIG_TAS2557_MISC
+	mutex_init(&pTAS2557->file_lock);
+	tas2557_register_misc(pTAS2557);
+#endif
+
+#ifdef ENABLE_TILOAD
+	tiload_driver_init(pTAS2557);
+#endif
+
+	hrtimer_init(&pTAS2557->mtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	pTAS2557->mtimer.function = temperature_timer_func;
+	INIT_WORK(&pTAS2557->mtimerwork, timer_work_routine);
+
+	nResult = request_firmware_nowait(THIS_MODULE, 1, pFWName,
+		pTAS2557->dev, GFP_KERNEL, pTAS2557, tas2557_fw_ready);
+
+    printk("[tas2557] tas2557_i2c_probe -----\n"); //[jinjia]Trace
+err:
+
+	return nResult;
+}
+
+static int tas2557_i2c_remove(struct i2c_client *pClient)
+{
+	struct tas2557_priv *pTAS2557 = i2c_get_clientdata(pClient);
+
+	dev_info(pTAS2557->dev, "%s\n", __func__);
+
+#ifdef CONFIG_TAS2557_CODEC
+	tas2557_deregister_codec(pTAS2557);
+	mutex_destroy(&pTAS2557->codec_lock);
+#endif
+
+#ifdef CONFIG_TAS2557_MISC
+	tas2557_deregister_misc(pTAS2557);
+	mutex_destroy(&pTAS2557->file_lock);
+#endif
+
+	mutex_destroy(&pTAS2557->dev_lock);
+	return 0;
+}
+
+static const struct i2c_device_id tas2557_i2c_id[] = {
+	{"tas2557", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, tas2557_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tas2557_of_match[] = {
+	{.compatible = "ti,tas2557"},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, tas2557_of_match);
+#endif
+
+static struct i2c_driver tas2557_i2c_driver = {
+	.driver = {
+			.name = "tas2557",
+			.owner = THIS_MODULE,
+#if defined(CONFIG_OF)
+			.of_match_table = of_match_ptr(tas2557_of_match),
+#endif
+		},
+	.probe = tas2557_i2c_probe,
+	.remove = tas2557_i2c_remove,
+	.id_table = tas2557_i2c_id,
+};
+
+module_i2c_driver(tas2557_i2c_driver);
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2557 I2C Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
+
+#endif
diff --git a/sound/soc/codecs/tas2557/tas2557.h b/sound/soc/codecs/tas2557/tas2557.h
new file mode 100644
index 0000000..a45c4ec
--- /dev/null
+++ b/sound/soc/codecs/tas2557/tas2557.h
@@ -0,0 +1,489 @@
+/*
+** =============================================================================
+** 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:
+**     tas2557.h
+**
+** Description:
+**     definitions and data structures for TAS2557 Android Linux driver
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2557_H
+#define _TAS2557_H
+
+#include <linux/regmap.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+
+/* Page Control Register */
+#define TAS2557_PAGECTL_REG			0
+
+/* Book Control Register (available in page0 of each book) */
+#define TAS2557_BOOKCTL_PAGE			0
+#define TAS2557_BOOKCTL_REG			127
+
+/* 0000 0000 0BBB BBBB BPPP PPPP PRRR RRRR */
+
+#define TAS2557_REG(book, page, reg)		((((unsigned int)book * 256 * 128) + \
+						 ((unsigned int)page * 128)) + reg)
+
+#define TAS2557_BOOK_ID(reg)			((unsigned char)(reg / (256 * 128)))
+#define TAS2557_PAGE_ID(reg)			((unsigned char)((reg % (256 * 128)) / 128))
+#define TAS2557_BOOK_REG(reg)			((unsigned char)(reg % (256 * 128)))
+#define TAS2557_PAGE_REG(reg)			((unsigned char)((reg % (256 * 128)) % 128))
+
+/* Book0, Page0 registers */
+#define TAS2557_SW_RESET_REG			TAS2557_REG(0, 0, 1)
+
+#define TAS2557_REV_PGID_REG			TAS2557_REG(0, 0, 3)
+#define TAS2557_PG_VERSION_1P0			0x80
+#define TAS2557_PG_VERSION_2P0			0x90
+#define TAS2557_PG_VERSION_2P1			0xa0
+
+#define TAS2557_POWER_CTRL1_REG			TAS2557_REG(0, 0, 4)
+#define TAS2557_POWER_CTRL2_REG			TAS2557_REG(0, 0, 5)
+
+#define TAS2557_SPK_CTRL_REG			TAS2557_REG(0, 0, 6)
+/* B0P0R6 - TAS2557_SPK_CTRL_REG */
+#define TAS2557_DAC_GAIN_MASK			(0xf << 3)
+#define TAS2557_DAC_GAIN_SHIFT			0x03
+
+#define TAS2557_MUTE_REG			TAS2557_REG(0, 0, 7)
+#define TAS2557_SNS_CTRL_REG			TAS2557_REG(0, 0, 8)
+#define TAS2557_ADC_INPUT_SEL_REG		TAS2557_REG(0, 0, 9)
+#define TAS2557_DBOOST_CTL_REG			TAS2557_REG(0, 0, 10)
+#define TAS2557_NONAME11_REG			TAS2557_REG(0, 0, 11)
+#define TAS2557_NONAME12_REG			TAS2557_REG(0, 0, 12)
+#define TAS2557_NONAME13_REG			TAS2557_REG(0, 0, 13)
+#define TAS2557_NONAME14_REG			TAS2557_REG(0, 0, 14)
+#define TAS2557_NONAME15_REG			TAS2557_REG(0, 0, 15)
+#define TAS2557_NONAME16_REG			TAS2557_REG(0, 0, 16)
+#define TAS2557_NONAME17_REG			TAS2557_REG(0, 0, 17)
+#define TAS2557_NONAME18_REG			TAS2557_REG(0, 0, 18)
+#define TAS2557_SAR_SAMPLING_TIME_REG		TAS2557_REG(0, 0, 19)
+#define TAS2557_SAR_ADC1_REG			TAS2557_REG(0, 0, 20)
+#define TAS2557_SAR_ADC2_REG			TAS2557_REG(0, 0, 21)	/* B0_P0_R0x15*/
+#define TAS2557_CRC_CHECKSUM_REG		TAS2557_REG(0, 0, 32)
+#define TAS2557_CRC_RESET_REG			TAS2557_REG(0, 0, 33)
+#define TAS2557_DSP_MODE_SELECT_REG		TAS2557_REG(0, 0, 34)
+#define TAS2557_SAFE_GUARD_REG			TAS2557_REG(0, 0, 37)
+#define TAS2557_ASI_CTL1_REG			TAS2557_REG(0, 0, 42)
+#define TAS2557_CLK_ERR_CTRL			TAS2557_REG(0, 0, 44)	/* B0_P0_R0x2c*/
+#define TAS2557_CLK_ERR_CTRL2			TAS2557_REG(0, 0, 45)	/* B0_P0_R0x2d*/
+#define TAS2557_CLK_ERR_CTRL3			TAS2557_REG(0, 0, 46)	/* B0_P0_R0x2e*/
+#define TAS2557_DBOOST_CFG_REG			TAS2557_REG(0, 0, 52)
+#define TAS2557_POWER_UP_FLAG_REG		TAS2557_REG(0, 0, 100)
+#define TAS2557_FLAGS_1				TAS2557_REG(0, 0, 104)	/* B0_P0_R0x68*/
+#define TAS2557_FLAGS_2				TAS2557_REG(0, 0, 108)	/* B0_P0_R0x6c*/
+
+/* Book0, Page1 registers */
+#define TAS2557_ASI1_DAC_FORMAT_REG		TAS2557_REG(0, 1, 1)
+#define TAS2557_ASI1_ADC_FORMAT_REG		TAS2557_REG(0, 1, 2)
+#define TAS2557_ASI1_OFFSET1_REG		TAS2557_REG(0, 1, 3)
+#define TAS2557_ASI1_ADC_PATH_REG		TAS2557_REG(0, 1, 7)
+#define TAS2557_ASI1_DAC_BCLK_REG		TAS2557_REG(0, 1, 8)
+#define TAS2557_ASI1_DAC_WCLK_REG		TAS2557_REG(0, 1, 9)
+#define TAS2557_ASI1_ADC_BCLK_REG		TAS2557_REG(0, 1, 10)
+#define TAS2557_ASI1_ADC_WCLK_REG		TAS2557_REG(0, 1, 11)
+#define TAS2557_ASI1_DIN_DOUT_MUX_REG		TAS2557_REG(0, 1, 12)
+#define TAS2557_ASI1_BDIV_CLK_SEL_REG		TAS2557_REG(0, 1, 13)
+#define TAS2557_ASI1_BDIV_CLK_RATIO_REG		TAS2557_REG(0, 1, 14)
+#define TAS2557_ASI1_WDIV_CLK_RATIO_REG		TAS2557_REG(0, 1, 15)
+#define TAS2557_ASI1_DAC_CLKOUT_REG		TAS2557_REG(0, 1, 16)
+#define TAS2557_ASI1_ADC_CLKOUT_REG		TAS2557_REG(0, 1, 17)
+#define TAS2557_ASI2_DAC_FORMAT_REG		TAS2557_REG(0, 1, 21)
+#define TAS2557_ASI2_ADC_FORMAT_REG		TAS2557_REG(0, 1, 22)
+#define TAS2557_ASI2_OFFSET1_REG		TAS2557_REG(0, 1, 23)
+#define TAS2557_ASI2_ADC_PATH_REG		TAS2557_REG(0, 1, 27)
+#define TAS2557_ASI2_DAC_BCLK_REG		TAS2557_REG(0, 1, 28)
+#define TAS2557_ASI2_DAC_WCLK_REG		TAS2557_REG(0, 1, 29)
+#define TAS2557_ASI2_ADC_BCLK_REG		TAS2557_REG(0, 1, 30)
+#define TAS2557_ASI2_ADC_WCLK_REG		TAS2557_REG(0, 1, 31)
+#define TAS2557_ASI2_DIN_DOUT_MUX_REG		TAS2557_REG(0, 1, 32)
+#define TAS2557_ASI2_BDIV_CLK_SEL_REG		TAS2557_REG(0, 1, 33)
+#define TAS2557_ASI2_BDIV_CLK_RATIO_REG		TAS2557_REG(0, 1, 34)
+#define TAS2557_ASI2_WDIV_CLK_RATIO_REG		TAS2557_REG(0, 1, 35)
+#define TAS2557_ASI2_DAC_CLKOUT_REG		TAS2557_REG(0, 1, 36)
+#define TAS2557_ASI2_ADC_CLKOUT_REG		TAS2557_REG(0, 1, 37)
+#define TAS2557_GPIO1_PIN_REG			TAS2557_REG(0, 1, 61)	/*B0_P1_R0x3d */
+#define TAS2557_GPIO2_PIN_REG			TAS2557_REG(0, 1, 62)	/*B0_P1_R0x3e */
+#define TAS2557_GPIO3_PIN_REG			TAS2557_REG(0, 1, 63)	/*B0_P1_R0x3f */
+#define TAS2557_GPIO4_PIN_REG			TAS2557_REG(0, 1, 64)	/*B0_P1_R0x40 */
+#define TAS2557_GPIO5_PIN_REG			TAS2557_REG(0, 1, 65)
+#define TAS2557_GPIO6_PIN_REG			TAS2557_REG(0, 1, 66)
+#define TAS2557_GPIO7_PIN_REG			TAS2557_REG(0, 1, 67)
+#define TAS2557_GPIO8_PIN_REG			TAS2557_REG(0, 1, 68)
+#define TAS2557_GPIO9_PIN_REG			TAS2557_REG(0, 1, 69)
+#define TAS2557_GPIO10_PIN_REG			TAS2557_REG(0, 1, 70)
+#define TAS2557_GPI_PIN_REG				TAS2557_REG(0, 1, 77)	/*B0_P1_R0x4d */
+#define TAS2557_GPIO_HIZ_CTRL1_REG		TAS2557_REG(0, 1, 79)
+#define TAS2557_GPIO_HIZ_CTRL2_REG		TAS2557_REG(0, 1, 80)	/*B0_P1_R0x50 */
+#define TAS2557_GPIO_HIZ_CTRL3_REG		TAS2557_REG(0, 1, 81)
+#define TAS2557_GPIO_HIZ_CTRL4_REG		TAS2557_REG(0, 1, 82)
+#define TAS2557_GPIO_HIZ_CTRL5_REG		TAS2557_REG(0, 1, 83)
+#define TAS2557_BIT_BANG_CTRL_REG		TAS2557_REG(0, 1, 87)
+#define TAS2557_BIT_BANG_OUT1_REG		TAS2557_REG(0, 1, 88)
+#define TAS2557_BIT_BANG_OUT2_REG		TAS2557_REG(0, 1, 89)
+#define TAS2557_BIT_BANG_IN1_REG		TAS2557_REG(0, 1, 90)
+#define TAS2557_BIT_BANG_IN2_REG		TAS2557_REG(0, 1, 91)
+#define TAS2557_BIT_BANG_IN3_REG		TAS2557_REG(0, 1, 92)
+#define TAS2557_PDM_IN_CLK_REG			TAS2557_REG(0, 1, 94)
+#define TAS2557_PDM_IN_PIN_REG			TAS2557_REG(0, 1, 95)
+#define TAS2557_ASIM_IFACE1_REG			TAS2557_REG(0, 1, 98)
+#define TAS2557_ASIM_FORMAT_REG			TAS2557_REG(0, 1, 99)
+#define TAS2557_ASIM_IFACE3_REG			TAS2557_REG(0, 1, 100)
+#define TAS2557_ASIM_IFACE4_REG			TAS2557_REG(0, 1, 101)
+#define TAS2557_ASIM_IFACE5_REG			TAS2557_REG(0, 1, 102)
+#define TAS2557_ASIM_IFACE6_REG			TAS2557_REG(0, 1, 103)
+#define TAS2557_ASIM_IFACE7_REG			TAS2557_REG(0, 1, 104)
+#define TAS2557_ASIM_IFACE8_REG			TAS2557_REG(0, 1, 105)
+#define TAS2557_CLK_HALT_REG			TAS2557_REG(0, 1, 106)	/* B0_P1_R0x6a */
+#define TAS2557_INT_GEN1_REG			TAS2557_REG(0, 1, 108)	/* B0_P1_R0x6c */
+#define TAS2557_INT_GEN2_REG			TAS2557_REG(0, 1, 109)	/* B0_P1_R0x6d */
+#define TAS2557_INT_GEN3_REG			TAS2557_REG(0, 1, 110)	/* B0_P1_R0x6e */
+#define TAS2557_INT_GEN4_REG			TAS2557_REG(0, 1, 111)	/* B0_P1_R0x6f */
+#define TAS2557_INT_MODE_REG			TAS2557_REG(0, 1, 114)	/* B0_P1_R0x72 */
+#define TAS2557_MAIN_CLKIN_REG			TAS2557_REG(0, 1, 115)
+#define TAS2557_PLL_CLKIN_REG			TAS2557_REG(0, 1, 116)
+#define TAS2557_CLKOUT_MUX_REG			TAS2557_REG(0, 1, 117)
+#define TAS2557_CLKOUT_CDIV_REG			TAS2557_REG(0, 1, 118)
+#define TAS2557_HACK_GP01_REG			TAS2557_REG(0, 1, 122)
+
+#define TAS2557_HACK01_REG			TAS2557_REG(0, 2, 10)
+
+#define TAS2557_ISENSE_THRESHOLD		TAS2557_REG(0, 50, 104)
+#define TAS2557_BOOSTON_EFFICIENCY		TAS2557_REG(0, 51, 16)
+#define TAS2557_BOOSTOFF_EFFICIENCY		TAS2557_REG(0, 51, 20)
+#define TAS2557_BOOST_HEADROOM			TAS2557_REG(0, 51, 24)
+#define TAS2557_THERMAL_FOLDBACK_REG	TAS2557_REG(0, 51, 100)
+
+#define TAS2557_SA_PG2P1_CHL_CTRL_REG	TAS2557_REG(0, 53, 20)	/* B0_P0x35_R0x14 */
+#define TAS2557_SA_COEFF_SWAP_REG		TAS2557_REG(0, 53, 44)	/* B0_P0x35_R0x2c */
+
+#define TAS2557_SA_PG1P0_CHL_CTRL_REG	TAS2557_REG(0, 58, 120)	/* B0_P0x3a_R0x78 */
+
+#define TAS2557_TEST_MODE_REG			TAS2557_REG(0, 253, 13)	/* B0_P0xfd_R0x0d */
+#define TAS2557_BROADCAST_REG			TAS2557_REG(0, 253, 54)	/* B0_P0xfd_R0x36 */
+#define TAS2557_CRYPTIC_REG			TAS2557_REG(0, 253, 71)
+#define TAS2557_PG2P1_CALI_R0_REG		TAS2557_REG(0x8c, 0x2f, 0x40)
+#define TAS2557_PG1P0_CALI_R0_REG		TAS2557_REG(0x8c, 0x2f, 0x28)
+#define TAS2557_PG2P1_CALI_T_REG		TAS2557_REG(0x8c, 0x30, 0x20)
+#define TAS2557_PG1P0_CALI_T_REG		TAS2557_REG(0x8c, 0x30, 0x08)
+
+#define TAS2557_DAC_INTERPOL_REG		TAS2557_REG(100, 0, 1)
+#define TAS2557_SOFT_MUTE_REG			TAS2557_REG(100, 0, 7)
+#define TAS2557_PLL_P_VAL_REG			TAS2557_REG(100, 0, 27)
+#define TAS2557_PLL_J_VAL_REG			TAS2557_REG(100, 0, 28)
+#define TAS2557_PLL_D_VAL_MSB_REG		TAS2557_REG(100, 0, 29)
+#define TAS2557_PLL_D_VAL_LSB_REG		TAS2557_REG(100, 0, 30)
+#define TAS2557_CLK_MISC_REG			TAS2557_REG(100, 0, 31)
+#define TAS2557_PLL_N_VAL_REG			TAS2557_REG(100, 0, 32)
+#define TAS2557_DAC_MADC_VAL_REG		TAS2557_REG(100, 0, 33)
+#define TAS2557_ISENSE_DIV_REG			TAS2557_REG(100, 0, 42)
+#define TAS2557_RAMP_CLK_DIV_MSB_REG		TAS2557_REG(100, 0, 43)
+#define TAS2557_RAMP_CLK_DIV_LSB_REG		TAS2557_REG(100, 0, 44)
+
+#define TAS2557_DIE_TEMP_REG			TAS2557_REG(130, 2, 124)	/* B0x82_P0x02_R0x7C */
+
+/* Bits */
+/* B0P0R4 - TAS2557_POWER_CTRL1_REG */
+#define TAS2557_SW_SHUTDOWN			(0x1 << 0)
+#define TAS2557_MADC_POWER_UP			(0x1 << 3)
+#define TAS2557_MDAC_POWER_UP			(0x1 << 4)
+#define TAS2557_NDIV_POWER_UP			(0x1 << 5)
+#define TAS2557_PLL_POWER_UP			(0x1 << 6)
+#define TAS2557_DSP_POWER_UP			(0x1 << 7)
+
+/* B0P0R5 - TAS2557_POWER_CTRL2_REG */
+#define TAS2557_VSENSE_ENABLE			(0x1 << 0)
+#define TAS2557_ISENSE_ENABLE			(0x1 << 1)
+#define TAS2557_BOOST_ENABLE			(0x1 << 5)
+#define TAS2557_CLASSD_ENABLE			(0x1 << 7)
+
+/* B0P0R7 - TAS2557_MUTE_REG */
+#define TAS2557_CLASSD_MUTE			(0x1 << 0)
+#define TAS2557_ISENSE_MUTE			(0x1 << 1)
+
+/* B0P253R13 - TAS2557_TEST_MODE_REG */
+#define TAS2557_TEST_MODE_ENABLE		(13)
+#define TAS2557_TEST_MODE_MASK			(0xf << 0)
+
+/* B0P253R71 - TAS2557_CRYPTIC_REG */
+#define TAS2557_OSC_TRIM_CAP(x)			((x & 0x3f) << 0)
+#define TAS2557_DISABLE_ENCRYPTION		(0x1 << 6)
+#define TAS2557_SL_COMP				(0x1 << 7)
+
+/* B0P1R115/6 - TAS2557_MAIN/PLL_CLKIN_REG */
+#define TAS2557_XXX_CLKIN_GPIO1			(0)
+#define TAS2557_XXX_CLKIN_GPIO2			(1)
+#define TAS2557_XXX_CLKIN_GPIO3			(2)
+#define TAS2557_XXX_CLKIN_GPIO4			(3)
+#define TAS2557_XXX_CLKIN_GPIO5			(4)
+#define TAS2557_XXX_CLKIN_GPIO6			(5)
+#define TAS2557_XXX_CLKIN_GPIO7			(6)
+#define TAS2557_XXX_CLKIN_GPIO8			(7)
+#define TAS2557_XXX_CLKIN_GPIO9			(8)
+#define TAS2557_XXX_CLKIN_GPIO10		(9)
+#define TAS2557_XXX_CLKIN_GPI1			(12)
+#define TAS2557_XXX_CLKIN_GPI2			(13)
+#define TAS2557_XXX_CLKIN_GPI3			(14)
+#define TAS2557_NDIV_CLKIN_PLL			(15)
+#define TAS2557_PLL_CLKIN_INT_OSC		(15)
+
+#define TAS2557_MCLK_CLKIN_SRC_GPIO1       (0)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO2       (1)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO3       (2)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO4       (3)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO5       (4)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO6       (5)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO7       (6)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO8       (7)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO9       (8)
+#define TAS2557_MCLK_CLKIN_SRC_GPIO10      (9)
+#define TAS2557_MCLK_CLKIN_SRC_GPI1        (12)
+#define TAS2557_MCLK_CLKIN_SRC_GPI2        (13)
+#define TAS2557_MCLK_CLKIN_SRC_GPI3        (14)
+
+#define TAS2557_FORMAT_I2S			(0x0 << 5)
+#define TAS2557_FORMAT_DSP			(0x1 << 5)
+#define TAS2557_FORMAT_RIGHT_J			(0x2 << 5)
+#define TAS2557_FORMAT_LEFT_J			(0x3 << 5)
+#define TAS2557_FORMAT_MONO_PCM			(0x4 << 5)
+#define TAS2557_FORMAT_MASK			(0x7 << 5)
+
+#define TAS2557_WORDLENGTH_16BIT		(0x0 << 3)
+#define TAS2557_WORDLENGTH_20BIT		(0x1 << 3)
+#define TAS2557_WORDLENGTH_24BIT		(0x2 << 3)
+#define TAS2557_WORDLENGTH_32BIT		(0x3 << 3)
+#define TAS2557_WORDLENGTH_MASK			TAS2557_WORDLENGTH_32BIT
+
+/* B100P0R7 - TAS2557_SOFT_MUTE_REG */
+#define TAS2557_PDM_SOFT_MUTE			(0x1 << 0)
+#define TAS2557_VSENSE_SOFT_MUTE		(0x1 << 1)
+#define TAS2557_ISENSE_SOFT_MUTE		(0x1 << 2)
+#define TAS2557_CLASSD_SOFT_MUTE		(0x1 << 3)
+
+/* B100P0R27 - TAS2557_PLL_P_VAL_REG */
+#define TAS2557_PLL_P_VAL_MASK			(0x3f << 0)
+
+/* B100P0R28 - TAS2557_PLL_J_VAL_REG */
+#define TAS2557_PLL_J_VAL_MASK			((unsigned int) (0x7f << 0))
+#define TAS2557_PLL_J_VAL_MASKX	0x00
+
+/* B100P0R29-30 - TAS2557_PLL_D_VAL_MSB/LSB_REG */
+#define TAS2557_PLL_D_MSB_VAL(x)		((x >> 8) & 0x3f)
+#define TAS2557_PLL_D_LSB_VAL(x)		(x & 0xff)
+
+/* B100P0R31 - TAS2557_CLK_MISC_REG */
+#define TAS2557_DSP_CLK_FROM_PLL		(0x1 << 5)
+
+#define TAS2557_FW_NAME     "tas2557_uCDSP.bin"
+#define TAS2557_PG1P0_FW_NAME     "tas2557_pg1p0_uCDSP.bin"
+
+#define	TAS2557_APP_ROM1MODE	0
+#define	TAS2557_APP_ROM2MODE	1
+#define	TAS2557_APP_TUNINGMODE	2
+#define	TAS2557_APP_ROM1_96KHZ	3
+#define	TAS2557_APP_ROM2_96KHZ	4
+#define	TAS2557_APP_RAMMODE		5
+
+#define	TAS2557_BOOST_OFF		0
+#define	TAS2557_BOOST_DEVA		1
+#define	TAS2557_BOOST_DEVB		2
+#define	TAS2557_BOOST_BOTH		3
+
+#define	ERROR_NONE			0x00000000
+#define	ERROR_PLL_ABSENT	0x00000001
+#define	ERROR_DEVA_I2C_COMM	0x00000002
+#define	ERROR_PRAM_CRCCHK	0x00000008
+#define	ERROR_YRAM_CRCCHK	0x00000010
+#define	ERROR_CLK_DET2		0x00000020
+#define	ERROR_CLK_DET1		0x00000040
+#define	ERROR_CLK_LOST		0x00000080
+#define	ERROR_BROWNOUT		0x00000100
+#define	ERROR_DIE_OVERTEMP	0x00000200
+#define	ERROR_CLK_HALT		0x00000400
+#define	ERROR_UNDER_VOLTAGE	0x00000800
+#define	ERROR_OVER_CURRENT	0x00001000
+#define	ERROR_CLASSD_PWR	0x00002000
+#define	ERROR_SAFE_GUARD	0x00004000
+#define	ERROR_FAILSAFE		0x40000000
+
+struct TBlock {
+	unsigned int mnType;
+	unsigned char mbPChkSumPresent;
+	unsigned char mnPChkSum;
+	unsigned char mbYChkSumPresent;
+	unsigned char mnYChkSum;
+	unsigned int mnCommands;
+	unsigned char *mpData;
+};
+
+struct TData {
+	char mpName[64];
+	char *mpDescription;
+	unsigned int mnBlocks;
+	struct TBlock *mpBlocks;
+};
+
+struct TProgram {
+	char mpName[64];
+	char *mpDescription;
+	unsigned char mnAppMode;
+	unsigned short mnBoost;
+	struct TData mData;
+};
+
+struct TPLL {
+	char mpName[64];
+	char *mpDescription;
+	struct TBlock mBlock;
+};
+
+struct TConfiguration {
+	char mpName[64];
+	char *mpDescription;
+	unsigned int mnDevices;
+	unsigned int mnProgram;
+	unsigned int mnPLL;
+	unsigned int mnSamplingRate;
+	unsigned char mnPLLSrc;
+	unsigned int mnPLLSrcRate;
+	struct TData mData;
+};
+
+struct TCalibration {
+	char mpName[64];
+	char *mpDescription;
+	unsigned int mnProgram;
+	unsigned int mnConfiguration;
+	struct TData mData;
+};
+
+struct TFirmware {
+	unsigned int mnFWSize;
+	unsigned int mnChecksum;
+	unsigned int mnPPCVersion;
+	unsigned int mnFWVersion;
+	unsigned int mnDriverVersion;
+	unsigned int mnTimeStamp;
+	char mpDDCName[64];
+	char *mpDescription;
+	unsigned int mnDeviceFamily;
+	unsigned int mnDevice;
+	unsigned int mnPLLs;
+	struct TPLL *mpPLLs;
+	unsigned int mnPrograms;
+	struct TProgram *mpPrograms;
+	unsigned int mnConfigurations;
+	struct TConfiguration *mpConfigurations;
+	unsigned int mnCalibrations;
+	struct TCalibration *mpCalibrations;
+};
+
+struct tas2557_register {
+	int book;
+	int page;
+	int reg;
+};
+
+struct tas2557_priv {
+	struct device *dev;
+	struct regmap *mpRegmap;
+	int mnPGID;
+	int mnResetGPIO;
+	struct mutex dev_lock;
+	struct TFirmware *mpFirmware;
+	struct TFirmware *mpCalFirmware;
+	unsigned int mnCurrentProgram;
+	unsigned int mnCurrentSampleRate;
+	unsigned int mnNewConfiguration;
+	unsigned int mnCurrentConfiguration;
+	unsigned int mnCurrentCalibration;
+	unsigned char mnCurrentBook;
+	unsigned char mnCurrentPage;
+	bool mbTILoadActive;
+	bool mbPowerUp;
+	bool mbLoadConfigurationPrePowerUp;
+	bool mbLoadCalibrationPostPowerUp;
+	bool mbCalibrationLoaded;
+	int (*read)(struct tas2557_priv *pTAS2557,
+		unsigned int reg,
+		unsigned int *pValue);
+	int (*write)(struct tas2557_priv *pTAS2557,
+		unsigned int reg,
+		unsigned int Value);
+	int (*bulk_read)(struct tas2557_priv *pTAS2557,
+		unsigned int reg,
+		unsigned char *pData,
+		unsigned int len);
+	int (*bulk_write)(struct tas2557_priv *pTAS2557,
+		unsigned int reg,
+		unsigned char *pData,
+		unsigned int len);
+	int (*update_bits)(struct tas2557_priv *pTAS2557,
+		unsigned int reg,
+		unsigned int mask,
+		unsigned int value);
+	int (*set_config)(struct tas2557_priv *pTAS2557,
+		int config);
+	int (*set_calibration)(struct tas2557_priv *pTAS2557,
+		int calibration);
+	void (*clearIRQ)(struct tas2557_priv *pTAS2557);
+	void (*enableIRQ)(struct tas2557_priv *pTAS2557, bool enable, bool startup_chk);
+	void (*hw_reset)(struct tas2557_priv *pTAS2557);
+	/* device is working, but system is suspended */
+	int (*runtime_suspend)(struct tas2557_priv *pTAS2557);
+	int (*runtime_resume)(struct tas2557_priv *pTAS2557);
+
+	int mnGpioINT;
+	struct delayed_work irq_work;
+	unsigned int mnIRQ;
+	bool mbIRQEnable;
+	unsigned char mnI2SBits;
+
+
+	/* for low temperature check */
+	unsigned int mnDevGain;
+	unsigned int mnDevCurrentGain;
+	unsigned int mnDieTvReadCounter;
+	struct hrtimer mtimer;
+	struct work_struct mtimerwork;
+
+	/* device is working, but system is suspended */
+	bool mbRuntimeSuspend;
+
+	unsigned int mnErrCode;
+	unsigned int mnRestart;
+
+	/* for configurations with maximum TLimit 0x7fffffff,
+	 * bypass calibration update, usually used in factory test
+	*/
+	bool mbBypassTMax;
+
+	unsigned int mnEdge;
+
+#ifdef CONFIG_TAS2557_CODEC
+	struct mutex codec_lock;
+#endif
+
+#ifdef CONFIG_TAS2557_MISC
+	int mnDBGCmd;
+	int mnCurrentReg;
+	struct mutex file_lock;
+#endif
+
+};
+
+#endif /* _TAS2557_H */
diff --git a/sound/soc/codecs/tas2557/tiload.c b/sound/soc/codecs/tas2557/tiload.c
new file mode 100644
index 0000000..1719bc4
--- /dev/null
+++ b/sound/soc/codecs/tas2557/tiload.c
@@ -0,0 +1,409 @@
+/*
+** =============================================================================
+** 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:
+**     tiload.c
+**
+** Description:
+**     utility for TAS2557 Android in-system tuning
+**
+** =============================================================================
+*/
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+
+#include "tiload.h"
+
+/* enable debug prints in the driver */
+#define DEBUG
+
+static struct cdev *tiload_cdev;
+static int tiload_major; /* Dynamic allocation of Mjr No. */
+static int tiload_opened; /* Dynamic allocation of Mjr No. */
+static struct tas2557_priv *g_TAS2557;
+struct class *tiload_class;
+static unsigned int magic_num;
+
+static char gPage;
+static char gBook;
+/******************************** Debug section *****************************/
+
+
+/*----------------------------------------------------------------------------
+ * Function : tiload_open
+ *
+ * Purpose  : open method for tiload programming interface
+ *----------------------------------------------------------------------------
+ */
+static int tiload_open(struct inode *in, struct file *filp)
+{
+	struct tas2557_priv *pTAS2557 = g_TAS2557;
+
+	dev_info(pTAS2557->dev, "%s\n", __func__);
+
+	if (tiload_opened) {
+		dev_info(pTAS2557->dev, "%s device is already opened\n", "tiload");
+		return -EINVAL;
+	}
+	filp->private_data = (void *)pTAS2557;
+	tiload_opened++;
+	return 0;
+}
+
+/*----------------------------------------------------------------------------
+ * Function : tiload_release
+ *
+ * Purpose  : close method for tiload programming interface
+ *----------------------------------------------------------------------------
+ */
+static int tiload_release(struct inode *in, struct file *filp)
+{
+	struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data;
+
+	dev_info(pTAS2557->dev, "%s\n", __func__);
+	filp->private_data = NULL;
+	tiload_opened--;
+	return 0;
+}
+
+#define MAX_LENGTH 128
+/*----------------------------------------------------------------------------
+ * Function : tiload_read
+ *
+ * Purpose  : read from codec
+ *----------------------------------------------------------------------------
+ */
+static ssize_t tiload_read(struct file *filp, char __user *buf,
+	size_t count, loff_t *offset)
+{
+	struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data;
+	static char rd_data[MAX_LENGTH + 1];
+	unsigned int nCompositeRegister = 0, Value = 0;
+	char reg_addr;
+	size_t size;
+	int ret = 0;
+#ifdef DEBUG
+	/* int i; */
+#endif
+
+	dev_info(pTAS2557->dev, "%s\n", __func__);
+	if (count > MAX_LENGTH) {
+		dev_err(pTAS2557->dev, "Max %d bytes can be read\n", MAX_LENGTH);
+		return -EINVAL;
+	}
+
+	/* copy register address from user space  */
+	size = copy_from_user(&reg_addr, buf, 1);
+	if (size != 0) {
+		dev_err(pTAS2557->dev, "read: copy_from_user failure\n");
+		return -EINVAL;
+	}
+
+	size = count;
+
+	nCompositeRegister = BPR_REG(gBook, gPage, reg_addr);
+	if (count == 1) {
+		ret =
+			pTAS2557->read(pTAS2557, 0x80000000 | nCompositeRegister, &Value);
+		if (ret >= 0)
+			rd_data[0] = (char) Value;
+	} else if (count > 1) {
+		ret =
+			pTAS2557->bulk_read(pTAS2557, 0x80000000 | nCompositeRegister,
+			rd_data, size);
+	}
+	if (ret < 0)
+		dev_err(pTAS2557->dev, "%s, %d, ret=%d, count=%zu error happen!\n",
+			__func__, __LINE__, ret, count);
+
+#ifdef DEBUG
+	dev_info(pTAS2557->dev, "read size = %d, reg_addr= %x , count = %d\n",
+		(int) size, reg_addr, (int) count);
+/*	for (i = 0; i < (int) size; i++) {
+*		dev_dbg(pTAS2557->dev, "rd_data[%d]=%x\n", i, rd_data[i]);
+*	}
+*/
+#endif
+	if (size != count)
+		dev_err(pTAS2557->dev, "read %d registers from the codec\n", (int) size);
+
+	if (copy_to_user(buf, rd_data, size) != 0) {
+		dev_err(pTAS2557->dev, "copy_to_user failed\n");
+		return -EINVAL;
+	}
+
+	return size;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : tiload_write
+ *
+ * Purpose  : write to codec
+ *----------------------------------------------------------------------------
+ */
+static ssize_t tiload_write(struct file *filp, const char __user *buf,
+	size_t count, loff_t *offset)
+{
+	struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data;
+	static char wr_data[MAX_LENGTH + 1];
+	char *pData = wr_data;
+	size_t size;
+	unsigned int nCompositeRegister = 0;
+	unsigned int nRegister;
+	int ret = 0;
+#ifdef DEBUG
+	/* int i; */
+#endif
+	dev_info(pTAS2557->dev, "%s\n", __func__);
+
+	if (count > MAX_LENGTH) {
+		dev_err(pTAS2557->dev, "Max %d bytes can be read\n", MAX_LENGTH);
+		return -EINVAL;
+	}
+
+	/* copy buffer from user space  */
+	size = copy_from_user(wr_data, buf, count);
+	if (size != 0) {
+		dev_err(pTAS2557->dev, "copy_from_user failure %d\n", (int) size);
+		return -EINVAL;
+	}
+#ifdef DEBUG
+	dev_info(pTAS2557->dev, "write size = %zu\n", count);
+/* for (i = 0; i < (int) count; i++) {
+*		dev_info(pTAS2557->dev, "wr_data[%d]=%x\n", i, wr_data[i]);
+*	}
+*/
+#endif
+	nRegister = wr_data[0];
+	size = count;
+	if ((nRegister == 127) && (gPage == 0)) {
+		gBook = wr_data[1];
+		return size;
+	}
+
+	if (nRegister == 0) {
+		gPage = wr_data[1];
+		pData++;
+		count--;
+	}
+
+	nCompositeRegister = BPR_REG(gBook, gPage, nRegister);
+	if (count == 2) {
+		ret =
+			pTAS2557->write(pTAS2557, 0x80000000 | nCompositeRegister,
+			pData[1]);
+	} else if (count > 2) {
+		ret =
+			pTAS2557->bulk_write(pTAS2557, 0x80000000 | nCompositeRegister,
+			&pData[1], count - 1);
+	}
+
+	if (ret < 0)
+		dev_err(pTAS2557->dev, "%s, %d, ret=%d, count=%zu, ERROR Happen\n", __func__,
+			__LINE__, ret, count);
+
+	return size;
+}
+
+static void tiload_route_IO(struct tas2557_priv *pTAS2557, unsigned int bLock)
+{
+	if (bLock)
+		pTAS2557->write(pTAS2557, 0xAFFEAFFE, 0xBABEBABE);
+	else
+		pTAS2557->write(pTAS2557, 0xBABEBABE, 0xAFFEAFFE);
+}
+
+static long tiload_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data;
+	long num = 0;
+	void __user *argp = (void __user *) arg;
+	int val;
+	struct BPR bpr;
+
+	dev_info(pTAS2557->dev, "%s, cmd=0x%x\n", __func__, cmd);
+/*  if (_IOC_TYPE(cmd) != TILOAD_IOC_MAGIC)
+ *      return -ENOTTY;
+ */
+
+	switch (cmd) {
+	case TILOAD_IOMAGICNUM_GET:
+		num = copy_to_user(argp, &magic_num, sizeof(int));
+		break;
+	case TILOAD_IOMAGICNUM_SET:
+		num = copy_from_user(&magic_num, argp, sizeof(int));
+		dev_info(pTAS2557->dev, "TILOAD_IOMAGICNUM_SET\n");
+		tiload_route_IO(pTAS2557, magic_num);
+		break;
+	case TILOAD_BPR_READ:
+		break;
+	case TILOAD_BPR_WRITE:
+		num = copy_from_user(&bpr, argp, sizeof(struct BPR));
+		dev_info(pTAS2557->dev, "TILOAD_BPR_WRITE: 0x%02X, 0x%02X, 0x%02X\n\r", bpr.nBook,
+			bpr.nPage, bpr.nRegister);
+		break;
+	case TILOAD_IOCTL_SET_CHL:
+		break;
+	case TILOAD_IOCTL_SET_CONFIG:
+		num = copy_from_user(&val, argp, sizeof(val));
+		pTAS2557->set_config(pTAS2557, val);
+		break;
+	case TILOAD_IOCTL_SET_CALIBRATION:
+		num = copy_from_user(&val, argp, sizeof(val));
+		pTAS2557->set_calibration(pTAS2557, val);
+		break;
+	default:
+		break;
+	}
+	return num;
+}
+
+#ifdef CONFIG_COMPAT
+static long tiload_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct tas2557_priv *pTAS2557 = (struct tas2557_priv *)filp->private_data;
+	long nResult = 0;
+
+	switch (cmd) {
+	case TILOAD_COMPAT_IOMAGICNUM_GET:
+		dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOMAGICNUM_GET=0x%x\n",
+			__func__, cmd);
+		nResult = tiload_ioctl(filp, TILOAD_IOMAGICNUM_GET,
+			(unsigned long) compat_ptr(arg));
+		break;
+
+	case TILOAD_COMPAT_IOMAGICNUM_SET:
+		dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOMAGICNUM_SET=0x%x\n",
+			__func__, cmd);
+		nResult = tiload_ioctl(filp, TILOAD_IOMAGICNUM_SET,
+			(unsigned long) compat_ptr(arg));
+		break;
+
+	case TILOAD_COMPAT_BPR_READ:
+		dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_BPR_READ=0x%x\n",
+			__func__, cmd);
+		nResult = tiload_ioctl(filp, TILOAD_BPR_READ,
+			(unsigned long) compat_ptr(arg));
+		break;
+
+	case TILOAD_COMPAT_BPR_WRITE:
+		dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_BPR_WRITE=0x%x\n",
+			__func__, cmd);
+		nResult = tiload_ioctl(filp, TILOAD_BPR_WRITE,
+			(unsigned long) compat_ptr(arg));
+		break;
+
+	case TILOAD_COMPAT_IOCTL_SET_CHL:
+		dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOCTL_SET_CHL=0x%x\n",
+			__func__, cmd);
+		nResult = tiload_ioctl(filp, TILOAD_IOCTL_SET_CHL,
+			(unsigned long) compat_ptr(arg));
+		break;
+
+	case TILOAD_COMPAT_IOCTL_SET_CONFIG:
+		dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOCTL_SET_CONFIG=0x%x\n",
+			__func__, cmd);
+		nResult = tiload_ioctl(filp, TILOAD_IOCTL_SET_CONFIG,
+			(unsigned long) compat_ptr(arg));
+		break;
+
+	case TILOAD_COMPAT_IOCTL_SET_CALIBRATION:
+		dev_info(pTAS2557->dev, "%s, TILOAD_COMPAT_IOCTL_SET_CALIBRATION=0x%x\n",
+			__func__, cmd);
+		nResult = tiload_ioctl(filp, TILOAD_IOCTL_SET_CALIBRATION,
+			(unsigned long) compat_ptr(arg));
+		break;
+
+	default:
+		dev_err(pTAS2557->dev, "%s, unsupport compat ioctl=0x%x\n",
+			__func__, cmd);
+		break;
+	}
+
+	return nResult;
+}
+#endif
+
+/*********** File operations structure for tiload *************/
+static const struct file_operations tiload_fops = {
+	.owner = THIS_MODULE,
+	.open = tiload_open,
+	.release = tiload_release,
+	.read = tiload_read,
+	.write = tiload_write,
+	.unlocked_ioctl = tiload_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = tiload_compat_ioctl,
+#endif
+};
+
+/*----------------------------------------------------------------------------
+ * Function : tiload_driver_init
+ *
+ * Purpose  : Register a char driver for dynamic tiload programming
+ *----------------------------------------------------------------------------
+ */
+int tiload_driver_init(struct tas2557_priv *pTAS2557)
+{
+	int result;
+	dev_t dev = MKDEV(tiload_major, 0);
+
+	g_TAS2557 = pTAS2557;
+
+	dev_info(pTAS2557->dev, "%s\n", __func__);
+
+	result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
+	if (result < 0) {
+		dev_err(pTAS2557->dev, "cannot allocate major number %d\n", tiload_major);
+		return result;
+	}
+	tiload_class = class_create(THIS_MODULE, DEVICE_NAME);
+	tiload_major = MAJOR(dev);
+	dev_info(pTAS2557->dev, "allocated Major Number: %d\n", tiload_major);
+
+	tiload_cdev = cdev_alloc();
+	cdev_init(tiload_cdev, &tiload_fops);
+	tiload_cdev->owner = THIS_MODULE;
+	tiload_cdev->ops = &tiload_fops;
+
+	if (device_create(tiload_class, NULL, dev, NULL, "tiload_node") == NULL)
+		dev_err(pTAS2557->dev, "Device creation failed\n");
+
+	if (cdev_add(tiload_cdev, dev, 1) < 0) {
+		dev_err(pTAS2557->dev, "tiload_driver: cdev_add failed\n");
+		unregister_chrdev_region(dev, 1);
+		tiload_cdev = NULL;
+		return 1;
+	}
+	dev_info(pTAS2557->dev, "Registered TiLoad driver, Major number: %d\n", tiload_major);
+	/* class_device_create(tiload_class, NULL, dev, NULL, DEVICE_NAME, 0); */
+	return 0;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("Utility for TAS2557 Android in-system tuning");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tas2557/tiload.h b/sound/soc/codecs/tas2557/tiload.h
new file mode 100644
index 0000000..7468acf
--- /dev/null
+++ b/sound/soc/codecs/tas2557/tiload.h
@@ -0,0 +1,65 @@
+/*
+** =============================================================================
+** 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:
+**     tiload.h
+**
+** Description:
+**     header file for tiload.c
+**
+** =============================================================================
+*/
+
+#ifndef _TILOAD_H
+#define _TILOAD_H
+
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+#endif
+
+#include "tas2557.h"
+
+#define BPR_REG(book, page, reg)		(((book * 256 * 128) + \
+						 (page * 128)) + reg)
+
+/* typedefs required for the included header files */
+struct BPR {
+	unsigned char nBook;
+	unsigned char nPage;
+	unsigned char nRegister;
+};
+
+/* defines */
+#define DEVICE_NAME     "tiload_node"
+
+#define TILOAD_IOC_MAGIC   0xE0
+#define TILOAD_IOMAGICNUM_GET			_IOR(TILOAD_IOC_MAGIC, 1, int)
+#define TILOAD_IOMAGICNUM_SET			_IOW(TILOAD_IOC_MAGIC, 2, int)
+#define TILOAD_BPR_READ					_IOR(TILOAD_IOC_MAGIC, 3, struct BPR)
+#define TILOAD_BPR_WRITE				_IOW(TILOAD_IOC_MAGIC, 4, struct BPR)
+#define TILOAD_IOCTL_SET_CHL			_IOW(TILOAD_IOC_MAGIC, 5, int)
+#define TILOAD_IOCTL_SET_CONFIG			_IOW(TILOAD_IOC_MAGIC, 6, int)
+#define TILOAD_IOCTL_SET_CALIBRATION	_IOW(TILOAD_IOC_MAGIC, 7, int)
+
+#ifdef CONFIG_COMPAT
+#define TILOAD_COMPAT_IOMAGICNUM_GET		_IOR(TILOAD_IOC_MAGIC, 1, compat_int_t)
+#define TILOAD_COMPAT_IOMAGICNUM_SET		_IOW(TILOAD_IOC_MAGIC, 2, compat_int_t)
+#define TILOAD_COMPAT_BPR_READ				_IOR(TILOAD_IOC_MAGIC, 3, struct BPR)
+#define TILOAD_COMPAT_BPR_WRITE				_IOW(TILOAD_IOC_MAGIC, 4, struct BPR)
+#define TILOAD_COMPAT_IOCTL_SET_CHL			_IOW(TILOAD_IOC_MAGIC, 5, compat_int_t)
+#define TILOAD_COMPAT_IOCTL_SET_CONFIG		_IOW(TILOAD_IOC_MAGIC, 6, compat_int_t)
+#define TILOAD_COMPAT_IOCTL_SET_CALIBRATION	_IOW(TILOAD_IOC_MAGIC, 7, compat_int_t)
+#endif
+
+int tiload_driver_init(struct tas2557_priv *pTAS2557);
+
+#endif