]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - tas2557sw-android/tas2557-android-driver.git/blobdiff - tas2557-core.c
Change Magic Number for kernel 4.19 support
[tas2557sw-android/tas2557-android-driver.git] / tas2557-core.c
index a680b14d7b8752434beb0467f3d9b8404f8301b3..444693069baf0a624e67f50d4161860059c09f93 100755 (executable)
@@ -3,17 +3,13 @@
 ** 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 
+** 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.
 **
-** You should have received a copy of the GNU General Public License along with
-** this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
-** Street, Fifth Floor, Boston, MA 02110-1301, USA.
-**
 ** File:
 **     tas2557-core.c
 **
 #include <linux/slab.h>
 #include <linux/syscalls.h>
 #include <linux/fcntl.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
+#include <linux/crc8.h>
 
 #include "tas2557.h"
 #include "tas2557-core.h"
 
-#define TAS2557_CAL_NAME    "/data/tas2557_cal.bin"
+#define        PPC_DRIVER_CRCCHK                       0x00000200
+#define        PPC_DRIVER_CONFDEV                      0x00000300
+#define        PPC_DRIVER_MTPLLSRC                     0x00000400
+#define        PPC_DRIVER_CFGDEV_NONCRC        0x00000101
 
-//set default PLL CLKIN to GPI2 (MCLK) = 0x00
-#define TAS2557_DEFAULT_PLL_CLKIN 0x00
+#define TAS2557_CAL_NAME    "/data/tas2557_cal.bin"
+#define RESTART_MAX 3
 
-static void tas2557_load_calibration(struct tas2557_priv *pTAS2557,
+static int tas2557_load_calibration(struct tas2557_priv *pTAS2557,
        char *pFileName);
-static void tas2557_load_data(struct tas2557_priv *pTAS2557, TData * pData,
+static int tas2557_load_data(struct tas2557_priv *pTAS2557, struct TData *pData,
        unsigned int nType);
-static void tas2557_load_block(struct tas2557_priv *pTAS2557, TBlock * pBlock);
-static void tas2557_load_configuration(struct tas2557_priv *pTAS2557,
+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 FW_ERR_HEADER -1
-#define FW_ERR_SIZE -2
-
 #define TAS2557_BLOCK_PLL                              0x00
 #define TAS2557_BLOCK_PGM_ALL                  0x0d
 #define TAS2557_BLOCK_PGM_DEV_A                        0x01
@@ -73,96 +71,56 @@ static void tas2557_load_configuration(struct tas2557_priv *pTAS2557,
 #define TAS2557_BLOCK_CFG_PRE_DEV_B            0x0b
 #define TAS2557_BLOCK_CFG_POST                 0x05
 #define TAS2557_BLOCK_CFG_POST_POWER   0x06
-#define TAS2557_BLOCK_CFG_CAL_A                        0x10
-#define TAS2557_BLOCK_CFG_CAL_B                        0x20
-
-static unsigned int p_tas2557_dboost_data[] = {
-       TAS2557_DBOOST_CTL_REG, 0x08,   //enable, 0x0c=disable
-       TAS2557_DBOOST_CFG_REG, 0x03,   //threshold -18dB +hysteresis 4dB
-       0xFFFFFFFF, 0xFFFFFFFF
-};
-
-//This is only needed for PG1.0
-static unsigned int p_tas2557_vpred_comp_data[] =
-{
-//Reverse attenuation compensation for Vpred 0.5dB
-       TAS2557_VPRED_COMP_REG, 0x04, 0x43, 0xca, 0xd0, 0x22,
-       0xFFFFFFFF, 0xFFFFFFFF
-};
-
-//This is only needed for PG1.0.
-static unsigned int p_tas2557_thermal_foldback_data[] = {
-       TAS2557_THERMAL_FOLDBACK_REG, 0x04, 0x48, 0x00, 0x00, 0x00,     //disable
-       0xFFFFFFFF, 0xFFFFFFFF
-};
-
-static unsigned int p_tas2557_boost_8Ohm_data[] =
-{
-       TAS2557_BOOST_HEADROOM, 0x04, 0x04, 0xcc, 0xcc, 0xcc,   // boost headroom 600mv
-    TAS2557_BOOSTOFF_EFFICIENCY, 0x04, 0x67, 0xae,0x14,0x7a, //boost off efficiency 0.81
-       0xFFFFFFFF, 0xFFFFFFFF
-};
-
-static unsigned int p_tas2557_boost_6Ohm_data[] =
-{
-       TAS2557_BOOST_HEADROOM, 0x04, 0x06, 0x66, 0x66, 0x66, // boost headroom 800mv
-       0xFFFFFFFF, 0xFFFFFFFF
-};
 
-static unsigned int p_tas2557_boost_4Ohm_data[] =
-{
-       TAS2557_BOOST_HEADROOM, 0x04, 0x06, 0x66, 0x66, 0x66, // boost headroom 800mv
-       TAS2557_BOOSTON_EFFICIENCY, 0x04, 0x73, 0x33, 0x33, 0x33,
-       TAS2557_BOOSTOFF_EFFICIENCY, 0x04, 0x60, 0x00,0x00,0x00, //boost off efficiency 0.75
+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
 };
 
-/* This is only required for PG2.0*/
-static unsigned int p_tas2557_isense_threshold_data[] =
-{
-       TAS2557_ISENSE_THRESHOLD, 0x04, 0, 0, 0, 0,     // Make Isense threshold zero
+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
 };
 
-#define TAS2557_STARTUP_DATA_PLL_CLKIN_INDEX 3
 static unsigned int p_tas2557_startup_data[] = {
-       TAS2557_CLK_ERR_CTRL, 0x03,     //enable clock error detection
-       TAS2557_PLL_CLKIN_REG, TAS2557_DEFAULT_PLL_CLKIN,
-       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_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
+       TAS2557_MUTE_REG, 0x00,          /* unmute */
+       TAS2557_SOFT_MUTE_REG, 0x00,     /* soft unmute */
        0xFFFFFFFF, 0xFFFFFFFF
 };
 
 static unsigned int p_tas2557_shutdown_data[] = {
-       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
-       0xFFFFFFFF, 0xFFFFFFFF
-};
-
-#if 0
-static unsigned int p_tas2557_shutdown_clk_err[] = {
-       TAS2557_CLK_ERR_CTRL, 0x09,     //enable clock error detection on PLL
-       0xFFFFFFFF, 0xFFFFFFFF
-};
-#endif
-
-static unsigned int p_tas2557_mute_DSP_down_data[] = {
-       TAS2557_MUTE_REG, 0x03,         //mute
-       TAS2557_POWER_CTRL1_REG, 0x60,  //DSP power down
-       TAS2557_UDELAY, 0xFF,           //delay
+       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
 };
 
@@ -179,162 +137,439 @@ static int tas2557_dev_load_data(struct tas2557_priv *pTAS2557,
                nData = pData[n * 2 + 1];
                if (nRegister == TAS2557_UDELAY)
                        udelay(nData);
-               else if (nRegister != 0xFFFFFFFF){
+               else if (nRegister != 0xFFFFFFFF) {
                        ret = pTAS2557->write(pTAS2557, nRegister, nData);
-                       if(ret < 0) {
-                               dev_err(pTAS2557->dev, "Reg Write err %d\n", ret);
+                       if (ret < 0)
                                break;
-                       }
                }
                n++;
        } while (nRegister != 0xFFFFFFFF);
-       
        return ret;
 }
 
-static int tas2557_dev_load_blk_data(struct tas2557_priv *pTAS2557,
-                                 unsigned int *pData) {
-       unsigned int nRegister;
-       unsigned int *nData;
-       unsigned char Buf[128];
-       unsigned int nLength = 0;
-       unsigned int nLoop = 0;
-       unsigned int i =0;
-       unsigned int nSize = 0;
-       int ret = 0;
-
-       do{
-               nRegister = pData[nLength];
-               nSize = pData[nLength + 1];
-               nData = &pData[nLength + 2];
-               if (nRegister == TAS2557_MDELAY){
-                       mdelay(nData[0]);
-               }
-               else{
-                       if (nRegister != 0xFFFFFFFF){
-                               if(nSize > 128){ 
-                                       dev_err(pTAS2557->dev, 
-                                               "%s, Line=%d, invalid size, maximum is 128 bytes!\n", 
-                                               __FUNCTION__, __LINE__);
-                                       break;
-                               }
-                               
-                               if(nSize > 1){
-                                       for(i = 0; i < nSize; i++) Buf[i] = (unsigned char)nData[i];
-                                       ret = pTAS2557->bulk_write(pTAS2557, nRegister, Buf, nSize);
-                               }else if(nSize == 1){
-                                       ret = pTAS2557->write(pTAS2557,nRegister, nData[0]);
-                               }else{
-                                       dev_err(pTAS2557->dev, 
-                                               "%s, Line=%d,invalid size, minimum is 1 bytes!\n", 
-                                               __FUNCTION__, __LINE__);
-                               }
-                               
-                               if(ret < 0) break;
-                       }
-               }
-               nLength = nLength + 2 + pData[nLength+1] ;
-       } while (nRegister != 0xFFFFFFFF);
-       
-       return ret;
+int tas2557_configIRQ(struct tas2557_priv *pTAS2557)
+{
+       return tas2557_dev_load_data(pTAS2557, p_tas2557_irq_config);
 }
 
-int tas2557_setLoad(struct tas2557_priv *pTAS2557, int load)
+int tas2557_set_bit_rate(struct tas2557_priv *pTAS2557, unsigned int nBitRate)
 {
-       int ret = 0;
+       int ret = 0, n = -1;
 
-       ret = pTAS2557->write(pTAS2557, 
-                       TAS2557_SNS_CTRL_REG, (load&0x03)<<1);
-       if(ret<0)
-               goto err;
-       
-       switch(load)
-       {
-               case 0: //8Ohm
-               ret = tas2557_dev_load_blk_data(pTAS2557, 
-                       p_tas2557_boost_8Ohm_data);
+       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 1: //6Ohm
-               ret = tas2557_dev_load_blk_data(pTAS2557, 
-                       p_tas2557_boost_6Ohm_data);
+       case 24:
+               n = 2;
                break;
-               
-               case 2: //4Ohm
-               ret = tas2557_dev_load_blk_data(pTAS2557, 
-                       p_tas2557_boost_4Ohm_data);
+       case 32:
+               n = 3;
                break;
        }
-       
-err:   
-       
+
+       if (n >= 0)
+               ret = pTAS2557->update_bits(pTAS2557, TAS2557_ASI1_DAC_FORMAT_REG, 0x18, n<<3);
        return ret;
 }
 
-int tas2557_load_platdata(struct tas2557_priv *pTAS2557)
+int tas2557_get_bit_rate(struct tas2557_priv *pTAS2557, unsigned char *pBitRate)
 {
        int ret = 0;
-       
-       ret = tas2557_setLoad(pTAS2557, pTAS2557->mnLoad);
-       if(pTAS2557->mnDevChl == LEFT_CHANNEL){
-               pTAS2557->update_bits(pTAS2557, TAS2557_ASI_CTRL_REG, 0x06, 0x00);
-       }else if(pTAS2557->mnDevChl == LEFT_CHANNEL){
-               pTAS2557->update_bits(pTAS2557, TAS2557_ASI_CTRL_REG, 0x06, 0x02);
-       }
-               
+       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_load_default(struct tas2557_priv *pTAS2557)
+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 = tas2557_dev_load_blk_data(pTAS2557, p_tas2557_dboost_data);
-       if(ret < 0) goto err;
-       
-       /* This is not required for PG1.0 and 2.1, only PG2.0*/
-       if(pTAS2557->mnPGID 
-               == TAS2557_PG_VERSION_2P0)
-               ret = tas2557_dev_load_blk_data(pTAS2557, 
-                               p_tas2557_isense_threshold_data);
-                               
-       if(pTAS2557->mnPGID 
-               == TAS2557_PG_VERSION_1P0){
-               tas2557_dev_load_blk_data(pTAS2557, p_tas2557_vpred_comp_data);
-               tas2557_dev_load_blk_data(pTAS2557, p_tas2557_thermal_foldback_data);
-       }
-       
-       tas2557_load_platdata(pTAS2557);
-       
-err:
-       
+
+       ret = pTAS2557->update_bits(pTAS2557, TAS2557_SPK_CTRL_REG, TAS2557_DAC_GAIN_MASK,
+               (nGain<<TAS2557_DAC_GAIN_SHIFT));
        return ret;
 }
 
-void tas2557_enable(struct tas2557_priv *pTAS2557, bool bEnable)
+/*
+* 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)
+{
+       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);
+       tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_data);
+       pTAS2557->mbPowerUp = false;
+       pTAS2557->hw_reset(pTAS2557);
+       pTAS2557->write(pTAS2557, TAS2557_SW_RESET_REG, 0x01);
+       udelay(1000);
+       pTAS2557->write(pTAS2557, TAS2557_SPK_CTRL_REG, 0x04);
+       if (pTAS2557->mpFirmware != NULL)
+               tas2557_clear_firmware(pTAS2557->mpFirmware);
+}
+
+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;
+       struct TProgram *pProgram;
+
        dev_dbg(pTAS2557->dev, "Enable: %d\n", bEnable);
+
+       tas2557_get_die_temperature(pTAS2770, &nValue);
+       if(nValue == 0x80000000)
+       {
+               dev_err(pTAS2557->dev, "%s, thermal sensor is wrong, mute output\n", __func__);
+               goto end;
+       }
+
+       if ((pTAS2557->mpFirmware->mnPrograms == 0)
+               || (pTAS2557->mpFirmware->mnConfigurations == 0)) {
+               dev_err(pTAS2557->dev, "%s, firmware not loaded\n", __func__);
+               goto end;
+       }
+       /* 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(pTAS2559, 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");
-                       tas2557_dev_load_data(pTAS2557, p_tas2557_startup_data);
+                       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");
-                       tas2557_dev_load_data(pTAS2557, p_tas2557_unmute_data);
+                       nResult = tas2557_dev_load_data(pTAS2557, p_tas2557_unmute_data);
+                       if (nResult < 0)
+                               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->mbPowerUp = true;
+                       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");
-                       tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_data);
-                       //tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_clk_err);
+                       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)
 {
-       TConfiguration *pConfiguration;
+       int nResult = 0;
+       struct TConfiguration *pConfiguration;
        unsigned int nConfiguration;
 
        dev_dbg(pTAS2557->dev, "tas2557_setup_clocks: nSamplingRate = %d [Hz]\n",
@@ -343,14 +578,16 @@ int tas2557_set_sampling_rate(struct tas2557_priv *pTAS2557, unsigned int nSampl
        if ((!pTAS2557->mpFirmware->mpPrograms) ||
                (!pTAS2557->mpFirmware->mpConfigurations)) {
                dev_err(pTAS2557->dev, "Firmware not loaded\n");
-               return -EINVAL;
+               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);
-               return 0;
+               nResult = 0;
+               goto end;
        }
 
        for (nConfiguration = 0;
@@ -359,31 +596,33 @@ int tas2557_set_sampling_rate(struct tas2557_priv *pTAS2557, unsigned int nSampl
                pConfiguration =
                        &(pTAS2557->mpFirmware->mpConfigurations[nConfiguration]);
                if ((pConfiguration->mnSamplingRate == nSamplingRate)
-                       &&(pConfiguration->mnProgram == pTAS2557->mnCurrentProgram)){
+                       && (pConfiguration->mnProgram == pTAS2557->mnCurrentProgram)) {
                        dev_info(pTAS2557->dev,
                                "Found configuration: %s, with compatible sampling rate %d\n",
                                pConfiguration->mpName, nSamplingRate);
-                       tas2557_load_configuration(pTAS2557, nConfiguration, false);
-                       return 0;
+                       nResult = tas2557_load_configuration(pTAS2557, nConfiguration, false);
+                       goto end;
                }
        }
 
        dev_err(pTAS2557->dev, "Cannot find a configuration that supports sampling rate: %d\n",
                nSamplingRate);
 
-       return -EINVAL;
+end:
+
+       return nResult;
 }
 
-static void fw_print_header(struct tas2557_priv *pTAS2557, TFirmware * pFirmware)
+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 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);
+       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)
@@ -391,23 +630,22 @@ 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, 
-       TFirmware * pFirmware, unsigned char *pData,
-       unsigned int nSize)
+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 < 102) {
+
+       if (nSize < 104) {
                dev_err(pTAS2557->dev, "Firmware: Header too short");
-               return -1;
+               return -EINVAL;
        }
 
        if (memcmp(pData, pMagicNumber, 4)) {
                dev_err(pTAS2557->dev, "Firmware: Magic number doesn't match");
-               return -1;
+               return -EINVAL;
        }
-
        pData += 4;
 
        pFirmware->mnFWSize = fw_convert_number(pData);
@@ -423,8 +661,8 @@ static int fw_parse_header(struct tas2557_priv *pTAS2557,
        pData += 4;
 
        pFirmware->mnDriverVersion = fw_convert_number(pData);
-       pData += 4;             
-                       
+       pData += 4;
+
        pFirmware->mnTimeStamp = fw_convert_number(pData);
        pData += 4;
 
@@ -434,36 +672,34 @@ static int fw_parse_header(struct tas2557_priv *pTAS2557,
        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 -1;
+               return -EINVAL;
        }
 
        pFirmware->mnDeviceFamily = fw_convert_number(pData);
        pData += 4;
-
-       if(pFirmware->mnDeviceFamily != 0){
-               dev_err(pTAS2557->dev, 
+       if (pFirmware->mnDeviceFamily != 0) {
+               dev_err(pTAS2557->dev,
                        "deviceFamily %d, not TAS device", pFirmware->mnDeviceFamily);
-               return -1;
+               return -EINVAL;
        }
-       
+
        pFirmware->mnDevice = fw_convert_number(pData);
        pData += 4;
 
-       if(pFirmware->mnDevice != 2){
-               dev_err(pTAS2557->dev, 
-                       "device %d, not TAS2557", pFirmware->mnDevice);
-               return -1;
+       if (pFirmware->mnDevice != 2) {
+               dev_err(pTAS2557->dev,
+                       "device %d, not TAS2557 Dual Mono", pFirmware->mnDevice);
+               return -EINVAL;
        }
-       
-       fw_print_header(pTAS2557, pFirmware);
 
+       fw_print_header(pTAS2557, pFirmware);
        return pData - pDataStart;
 }
 
-static int fw_parse_block_data(TBlock * pBlock, unsigned char *pData)
+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;
@@ -471,17 +707,34 @@ static int fw_parse_block_data(TBlock * pBlock, unsigned char *pData)
        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(TData * pImageData, unsigned char *pData)
+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;
@@ -498,27 +751,31 @@ static int fw_parse_data(TData * pImageData, unsigned char *pData)
        pData += 2;
 
        pImageData->mpBlocks =
-               kmalloc(sizeof(TBlock) * pImageData->mnBlocks, GFP_KERNEL);
+               kmalloc(sizeof(struct TBlock) * pImageData->mnBlocks, GFP_KERNEL);
 
        for (nBlock = 0; nBlock < pImageData->mnBlocks; nBlock++) {
-               n = fw_parse_block_data(&(pImageData->mpBlocks[nBlock]), pData);
+               n = fw_parse_block_data(pTAS2557, pFirmware,
+                       &(pImageData->mpBlocks[nBlock]), pData);
                pData += n;
        }
-
        return pData - pDataStart;
 }
 
-static int fw_parse_pll_data(TFirmware * pFirmware, unsigned char *pData)
+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;
-       TPLL *pPLL;
+       struct TPLL *pPLL;
 
        pFirmware->mnPLLs = (pData[0] << 8) + pData[1];
        pData += 2;
 
-       pFirmware->mpPLLs = kmalloc(sizeof(TPLL) * pFirmware->mnPLLs, GFP_KERNEL);
+       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]);
 
@@ -529,25 +786,30 @@ static int fw_parse_pll_data(TFirmware * pFirmware, unsigned char *pData)
                pPLL->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
                pData += n + 1;
 
-               n = fw_parse_block_data(&(pPLL->mBlock), pData);
+               n = fw_parse_block_data(pTAS2557, pFirmware, &(pPLL->mBlock), pData);
                pData += n;
        }
 
+end:
        return pData - pDataStart;
 }
 
-static int fw_parse_program_data(TFirmware * pFirmware, unsigned char *pData)
+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;
-       TProgram *pProgram;
+       struct TProgram *pProgram;
 
        pFirmware->mnPrograms = (pData[0] << 8) + pData[1];
        pData += 2;
 
+       if (pFirmware->mnPrograms == 0)
+               goto end;
+
        pFirmware->mpPrograms =
-               kmalloc(sizeof(TProgram) * pFirmware->mnPrograms, GFP_KERNEL);
+               kmalloc(sizeof(struct TProgram) * pFirmware->mnPrograms, GFP_KERNEL);
        for (nProgram = 0; nProgram < pFirmware->mnPrograms; nProgram++) {
                pProgram = &(pFirmware->mpPrograms[nProgram]);
                memcpy(pProgram->mpName, pData, 64);
@@ -559,30 +821,35 @@ static int fw_parse_program_data(TFirmware * pFirmware, unsigned char *pData)
 
                pProgram->mnAppMode = pData[0];
                pData++;
-               
+
                pProgram->mnBoost = (pData[0] << 8) + pData[1];
                pData += 2;
-               
-               n = fw_parse_data(&(pProgram->mData), pData);
+
+               n = fw_parse_data(pTAS2557, pFirmware, &(pProgram->mData), pData);
                pData += n;
        }
 
+end:
+
        return pData - pDataStart;
 }
 
-static int fw_parse_configuration_data(TFirmware * pFirmware,
-       unsigned char *pData)
+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;
-       TConfiguration *pConfiguration;
+       struct TConfiguration *pConfiguration;
 
        pFirmware->mnConfigurations = (pData[0] << 8) + pData[1];
        pData += 2;
 
+       if (pFirmware->mnConfigurations == 0)
+               goto end;
+
        pFirmware->mpConfigurations =
-               kmalloc(sizeof(TConfiguration) * pFirmware->mnConfigurations,
+               kmalloc(sizeof(struct TConfiguration) * pFirmware->mnConfigurations,
                GFP_KERNEL);
        for (nConfiguration = 0; nConfiguration < pFirmware->mnConfigurations;
                nConfiguration++) {
@@ -594,6 +861,14 @@ static int fw_parse_configuration_data(TFirmware * pFirmware,
                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++;
 
@@ -603,25 +878,39 @@ static int fw_parse_configuration_data(TFirmware * pFirmware,
                pConfiguration->mnSamplingRate = fw_convert_number(pData);
                pData += 4;
 
-               n = fw_parse_data(&(pConfiguration->mData), pData);
+               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(TFirmware * pFirmware, unsigned char *pData)
+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;
-       TCalibration *pCalibration;
+       struct TCalibration *pCalibration;
 
        pFirmware->mnCalibrations = (pData[0] << 8) + pData[1];
        pData += 2;
 
+       if (pFirmware->mnCalibrations == 0)
+               goto end;
+
        pFirmware->mpCalibrations =
-               kmalloc(sizeof(TCalibration) * pFirmware->mnCalibrations, GFP_KERNEL);
+               kmalloc(sizeof(struct TCalibration) * pFirmware->mnCalibrations, GFP_KERNEL);
        for (nCalibration = 0;
                nCalibration < pFirmware->mnCalibrations;
                nCalibration++) {
@@ -639,71 +928,354 @@ int fw_parse_calibration_data(TFirmware * pFirmware, unsigned char *pData)
                pCalibration->mnConfiguration = pData[0];
                pData++;
 
-               n = fw_parse_block_data(&(pCalibration->mBlock), pData);
+               n = fw_parse_data(pTAS2557, pFirmware, &(pCalibration->mData), pData);
                pData += n;
        }
 
+end:
+
        return pData - pDataStart;
 }
 
 static int fw_parse(struct tas2557_priv *pTAS2557,
-       TFirmware * pFirmware,
-       unsigned char *pData,
-       unsigned int nSize)
+       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 FW_ERR_HEADER;
+               return -EINVAL;
        }
 
        if (nPosition >= nSize) {
                dev_err(pTAS2557->dev, "Firmware: Too short");
-               return FW_ERR_SIZE;
+               return -EINVAL;
        }
 
        pData += nPosition;
        nSize -= nPosition;
        nPosition = 0;
 
-       nPosition = fw_parse_pll_data(pFirmware, pData);
+       nPosition = fw_parse_pll_data(pTAS2557, pFirmware, pData);
 
        pData += nPosition;
        nSize -= nPosition;
        nPosition = 0;
 
-       nPosition = fw_parse_program_data(pFirmware, pData);
+       nPosition = fw_parse_program_data(pTAS2557, pFirmware, pData);
 
        pData += nPosition;
        nSize -= nPosition;
        nPosition = 0;
 
-       nPosition = fw_parse_configuration_data(pFirmware, pData);
+       nPosition = fw_parse_configuration_data(pTAS2557, pFirmware, pData);
 
        pData += nPosition;
        nSize -= nPosition;
        nPosition = 0;
 
        if (nSize > 64)
-               nPosition = fw_parse_calibration_data(pFirmware, pData);
-
+               nPosition = fw_parse_calibration_data(pTAS2557, pFirmware, pData);
        return 0;
 }
 
-static void tas2557_load_block(struct tas2557_priv *pTAS2557, TBlock * pBlock)
+
+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;
 
@@ -714,174 +1286,209 @@ static void tas2557_load_block(struct tas2557_priv *pTAS2557, TBlock * pBlock)
 
                nCommand++;
 
-               if (nOffset <= 0x7F){
-                       pTAS2557->write(pTAS2557, TAS2557_REG(nBook, nPage, nOffset),
-                               nData);
-               }else if (nOffset == 0x81) {
-                       unsigned int nSleep = (nBook << 8) + nPage;
+               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) {
+               } else if (nOffset == 0x85) {
                        pData += 4;
                        nLength = (nBook << 8) + nPage;
                        nBook = pData[0];
                        nPage = pData[1];
                        nOffset = pData[2];
-                       if (nLength > 1)
-                               pTAS2557->bulk_write(pTAS2557, TAS2557_REG(nBook, nPage,
-                                               nOffset), pData + 3, nLength);
-                       else
-                               pTAS2557->write(pTAS2557, TAS2557_REG(nBook, nPage, nOffset),
-                                       pData[3]);
+                       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 void tas2557_load_data(struct tas2557_priv *pTAS2557, TData * pData,
-       unsigned int nType)
+static int tas2557_load_data(struct tas2557_priv *pTAS2557, struct TData *pData, unsigned int nType)
 {
+       int nResult = 0;
        unsigned int nBlock;
-       TBlock *pBlock;
+       struct TBlock *pBlock;
 
        dev_dbg(pTAS2557->dev,
-               "TAS2557 load data: %s, Blocks = %d, Block Type = %d\n", pData->mpName,
-               pData->mnBlocks, nType);
+               "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)
-                       tas2557_load_block(pTAS2557, pBlock);
+               if (pBlock->mnType == nType) {
+                       nResult = tas2557_load_block(pTAS2557, pBlock);
+                       if (nResult < 0)
+                               break;
+               }
        }
+
+       return nResult;
 }
 
-static void tas2557_load_configuration(struct tas2557_priv *pTAS2557,
+static int tas2557_load_configuration(struct tas2557_priv *pTAS2557,
        unsigned int nConfiguration, bool bLoadSame)
 {
-       TConfiguration *pCurrentConfiguration;
-       TConfiguration *pNewConfiguration;
-       TPLL *pNewPLL;
+       int nResult = 0;
+       struct TConfiguration *pCurrentConfiguration = NULL;
+       struct TConfiguration *pNewConfiguration = NULL;
 
-       dev_dbg(pTAS2557->dev, "tas2557_load_configuration: %d\n", nConfiguration);
+       dev_dbg(pTAS2557->dev, "%s: %d\n", __func__, nConfiguration);
 
        if ((!pTAS2557->mpFirmware->mpPrograms) ||
                (!pTAS2557->mpFirmware->mpConfigurations)) {
                dev_err(pTAS2557->dev, "Firmware not loaded\n");
-               return;
+               nResult = 0;
+               goto end;
        }
 
        if (nConfiguration >= pTAS2557->mpFirmware->mnConfigurations) {
                dev_err(pTAS2557->dev, "Configuration %d doesn't exist\n",
                        nConfiguration);
-               return;
+               nResult = 0;
+               goto end;
        }
 
-       if ((nConfiguration == pTAS2557->mnCurrentConfiguration) && (!bLoadSame)) {
+       if ((!pTAS2557->mbLoadConfigurationPrePowerUp)
+               && (nConfiguration == pTAS2557->mnCurrentConfiguration)
+               && (!bLoadSame)) {
                dev_info(pTAS2557->dev, "Configuration %d is already loaded\n",
                        nConfiguration);
-               return;
+               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",
+               dev_err(pTAS2557->dev, "Configuration %d, %s doesn't share the same program as current %d\n",
                        nConfiguration, pNewConfiguration->mpName, pCurrentConfiguration->mnProgram);
-               return;
+               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",
+               dev_err(pTAS2557->dev, "Configuration %d, %s doesn't have a valid PLL index %d\n",
                        nConfiguration, pNewConfiguration->mpName, pNewConfiguration->mnPLL);
-               return;
+               nResult = 0;
+               goto end;
        }
-       
-       pNewPLL = &(pTAS2557->mpFirmware->mpPLLs[pNewConfiguration->mnPLL]);
 
        if (pTAS2557->mbPowerUp) {
-               if (pNewConfiguration->mnPLL != pCurrentConfiguration->mnPLL) {
-                       dev_dbg(pTAS2557->dev,
-                               "TAS2557 is powered up -> mute and power down DSP before loading new configuration\n");
-                       //tas2557_dev_load_data(pTAS2557, p_tas2557_mute_DSP_down_data);
-                       tas2557_dev_load_data(pTAS2557, p_tas2557_shutdown_data);
-                       dev_dbg(pTAS2557->dev, "TAS2557: load new PLL: %s, block data\n",
-                               pNewPLL->mpName);
-                       tas2557_load_block(pTAS2557, &(pNewPLL->mBlock));
-                       pTAS2557->mnCurrentSampleRate = pNewConfiguration->mnSamplingRate;
-                       dev_dbg(pTAS2557->dev,
-                               "load new configuration: %s, pre block data\n",
-                               pNewConfiguration->mpName);
-                       tas2557_load_data(pTAS2557, &(pNewConfiguration->mData),
-                               TAS2557_BLOCK_CFG_PRE_DEV_A);
-                       dev_dbg(pTAS2557->dev,
-                               "TAS2557: load new configuration: %s, coeff block data\n",
-                               pNewConfiguration->mpName);
-                       tas2557_load_data(pTAS2557, &(pNewConfiguration->mData),
-                               TAS2557_BLOCK_CFG_COEFF_DEV_A);
-                       dev_dbg(pTAS2557->dev, "TAS2557: power up TAS2557\n");
-                       tas2557_dev_load_data(pTAS2557, p_tas2557_startup_data);
-                       dev_dbg(pTAS2557->dev, "TAS2557: unmute TAS2557\n");
-                       tas2557_dev_load_data(pTAS2557, p_tas2557_unmute_data);
-               } else {
-                       dev_dbg(pTAS2557->dev,
-                               "TAS2557 is powered up, no change in PLL: load new configuration: %s, coeff block data\n",
-                               pNewConfiguration->mpName);
-                       tas2557_load_data(pTAS2557, &(pNewConfiguration->mData),
-                               TAS2557_BLOCK_CFG_COEFF_DEV_A); 
-               }
-                               
-               pTAS2557->mbLoadConfigurationPostPowerUp = false;
+               pTAS2557->mbLoadConfigurationPrePowerUp = false;
+               nResult = tas2557_load_coefficient(pTAS2557, pTAS2557->mnCurrentConfiguration, nConfiguration, true);
        } else {
                dev_dbg(pTAS2557->dev,
-                       "TAS2557 was powered down\n");
-               if (pNewConfiguration->mnPLL != pCurrentConfiguration->mnPLL) {
-                       dev_dbg(pTAS2557->dev, "TAS2557: load new PLL: %s, block data\n",
-                               pNewPLL->mpName);
-                       tas2557_load_block(pTAS2557, &(pNewPLL->mBlock));
-                       pTAS2557->mnCurrentSampleRate = pNewConfiguration->mnSamplingRate;
-                       dev_dbg(pTAS2557->dev,
-                               "load new configuration: %s, pre block data\n",
-                               pNewConfiguration->mpName);
-                       tas2557_load_data(pTAS2557, &(pNewConfiguration->mData),
-                               TAS2557_BLOCK_CFG_PRE_DEV_A);
-               }
-               
-               dev_dbg(pTAS2557->dev,
-                               "TAS2557: load new configuration: %s, coeff block data\n",
-                               pNewConfiguration->mpName);
-               tas2557_load_data(pTAS2557, &(pNewConfiguration->mData),
-                               TAS2557_BLOCK_CFG_COEFF_DEV_A); 
-               
-               pTAS2557->mbLoadConfigurationPostPowerUp = true;
+                       "TAS2557 was powered down, will load coefficient when power up\n");
+               pTAS2557->mbLoadConfigurationPrePowerUp = true;
+               pTAS2557->mnNewConfiguration = nConfiguration;
        }
 
-       pTAS2557->mnCurrentConfiguration = 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)
 {
-       TConfiguration *pConfiguration;
-       TProgram *pProgram;
+       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");
-               return -1;
+               nResult = -EINVAL;
+               goto end;
        }
 
        if (nConfiguration >= pTAS2557->mpFirmware->mnConfigurations) {
                dev_err(pTAS2557->dev, "Configuration %d doesn't exist\n",
                        nConfiguration);
-               return -1;
+               nResult = -EINVAL;
+               goto end;
        }
 
        pConfiguration = &(pTAS2557->mpFirmware->mpConfigurations[nConfiguration]);
@@ -892,64 +1499,77 @@ int tas2557_set_config(struct tas2557_priv *pTAS2557, int config)
                        "Configuration %d, %s with Program %d isn't compatible with existing Program %d, %s\n",
                        nConfiguration, pConfiguration->mpName, pConfiguration->mnProgram,
                        nProgram, pProgram->mpName);
-               return -1;
+               nResult = -EINVAL;
+               goto end;
        }
 
-       tas2557_load_configuration(pTAS2557, nConfiguration, false);
+       nResult = tas2557_load_configuration(pTAS2557, nConfiguration, false);
 
-       return 0;
+end:
+
+       return nResult;
 }
 
-void tas2557_clear_firmware(TFirmware *pFirmware)
+void tas2557_clear_firmware(struct TFirmware *pFirmware)
 {
        unsigned int n, nn;
-       if (!pFirmware) return;
-       if (pFirmware->mpDescription) kfree(pFirmware->mpDescription);  
 
-       for (n = 0; n < pFirmware->mnPLLs; n++)
-       {
-               kfree(pFirmware->mpPLLs[n].mpDescription);
-               kfree(pFirmware->mpPLLs[n].mBlock.mpData);
+       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);
        }
-       kfree(pFirmware->mpPLLs);
 
-       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);
+       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);
        }
-       kfree(pFirmware->mpPrograms);
 
-       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);
+       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);
        }
-       kfree(pFirmware->mpConfigurations);
 
-       for (n = 0; n < pFirmware->mnCalibrations; n++)
-       {
-               kfree(pFirmware->mpCalibrations[n].mpDescription);
-               kfree(pFirmware->mpCalibrations[n].mBlock.mpData);
+       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);
        }
-       kfree(pFirmware->mpCalibrations);
 
-       memset(pFirmware, 0x00, sizeof(TFirmware));
+       memset(pFirmware, 0x00, sizeof(struct TFirmware));
 }
 
-static void tas2557_load_calibration(struct tas2557_priv *pTAS2557,
-       char *pFileName)
+static int tas2557_load_calibration(struct tas2557_priv *pTAS2557,     char *pFileName)
 {
-       int nResult;
+       int nResult = 0;
+
        int nFile;
        mm_segment_t fs;
-       unsigned char pBuffer[512];
+       unsigned char pBuffer[1000];
        int nSize = 0;
 
        dev_dbg(pTAS2557->dev, "%s:\n", __func__);
@@ -962,7 +1582,7 @@ static void tas2557_load_calibration(struct tas2557_priv *pTAS2557,
                pFileName, nFile);
 
        if (nFile >= 0) {
-               nSize = sys_read(nFile, pBuffer, 512);
+               nSize = sys_read(nFile, pBuffer, 1000);
                sys_close(nFile);
        } else {
                dev_err(pTAS2557->dev, "TAS2557 cannot open calibration file: %s\n",
@@ -972,20 +1592,118 @@ static void tas2557_load_calibration(struct tas2557_priv *pTAS2557,
        set_fs(fs);
 
        if (!nSize)
-               return;
+               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) {
+       if (nResult)
                dev_err(pTAS2557->dev, "TAS2557 calibration file is corrupt\n");
-               return;
+       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;
+               }
        }
 
-       dev_info(pTAS2557->dev, "TAS2557 calibration: %d calibrations\n",
-               pTAS2557->mpCalFirmware->mnCalibrations);
+       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)
@@ -995,235 +1713,348 @@ void tas2557_fw_ready(const struct firmware *pFW, void *pContext)
        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
+
        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);
-               
-               nResult = tas2557_load_default(pTAS2557);
-               return;
+               goto end;
        }
 
-       if (pTAS2557->mpFirmware->mpConfigurations){
+       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);
+       }
 
+       nResult = fw_parse(pTAS2557, pTAS2557->mpFirmware, (unsigned char *)(pFW->data), pFW->size);
        release_firmware(pFW);
-       
-       if (nResult) {
+       if (nResult < 0) {
                dev_err(pTAS2557->dev, "firmware is corrupt\n");
-               return;
+               goto end;
        }
 
        if (!pTAS2557->mpFirmware->mnPrograms) {
                dev_err(pTAS2557->dev, "firmware contains no programs\n");
-               return;
+               nResult = -EINVAL;
+               goto end;
        }
-       
+
        if (!pTAS2557->mpFirmware->mnConfigurations) {
-               dev_err(pTAS2557->dev, 
-                       "firmware contains no configurations\n");
-               return;
+               dev_err(pTAS2557->dev, "firmware contains no configurations\n");
+               nResult = -EINVAL;
+               goto end;
        }
-       
-       if(nProgram >= pTAS2557->mpFirmware->mnPrograms){
-               dev_info(pTAS2557->dev, 
+
+       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);
 
-       tas2557_set_program(pTAS2557, nProgram);
-}      
+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)
+       unsigned int nProgram, int nConfig)
 {
-       TPLL *pPLL;
-       TConfiguration *pConfiguration;
+       struct TProgram *pProgram;
        unsigned int nConfiguration = 0;
        unsigned int nSampleRate = 0;
-       unsigned int Value = 0;
+       unsigned char nGain;
        bool bFound = false;
-       int nResult = -1;
+       int nResult = 0;
 
        if ((!pTAS2557->mpFirmware->mpPrograms) ||
                (!pTAS2557->mpFirmware->mpConfigurations)) {
                dev_err(pTAS2557->dev, "Firmware not loaded\n");
-               return -1;
+               nResult = 0;
+               goto end;
        }
-       
+
        if (nProgram >= pTAS2557->mpFirmware->mnPrograms) {
                dev_err(pTAS2557->dev, "TAS2557: Program %d doesn't exist\n",
-                       nConfiguration);
-               return -1;
-       }
-       
-       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{
+                       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++;
                        }
-               }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;
        }
-       
-       if (!bFound) {
-               dev_err(pTAS2557->dev, 
-                       "Program %d, no valid configuration found for sample rate %d, ignore\n",
-                       nProgram, nSampleRate);
-               return -1;
+
+       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_dev_load_data(pTAS2557, p_tas2557_mute_DSP_down_data);
-       if(nResult < 0){
-               dev_err(pTAS2557->dev, 
-                       "device register access error\n");
-               return -1;
-       } 
-               
-       pTAS2557->write(pTAS2557, TAS2557_SW_RESET_REG, 0x01);
 
-       udelay(1000);
-       pTAS2557->mnCurrentBook = 0;
-       pTAS2557->mnCurrentPage = 0;
-       
-       nResult = tas2557_load_default(pTAS2557);       
-       
-       dev_info(pTAS2557->dev, "load program %d\n", nProgram);
-       tas2557_load_data(pTAS2557,
-               &(pTAS2557->mpFirmware->mpPrograms[nProgram].mData),
-               TAS2557_BLOCK_PGM_DEV_A);
-
-       nResult = pTAS2557->read(pTAS2557, TAS2557_CRC_CHECKSUM_REG, &Value);
-       dev_info(pTAS2557->dev, "uCDSP Checksum: 0x%02x\n", Value);
-       
-       pTAS2557->mnCurrentConfiguration = nConfiguration;
-       pConfiguration =
-               &(pTAS2557->mpFirmware->mpConfigurations[nConfiguration]);
-       pPLL = &(pTAS2557->mpFirmware->mpPLLs[pConfiguration->mnPLL]);
-       dev_dbg(pTAS2557->dev,
-               "TAS2557 load PLL: %s block for Configuration %s\n",
-               pPLL->mpName, pConfiguration->mpName);
-       
-       tas2557_load_block(pTAS2557, &(pPLL->mBlock));
-       pTAS2557->mnCurrentSampleRate = pConfiguration->mnSamplingRate;
-       dev_dbg(pTAS2557->dev,
-               "load configuration %s conefficient pre block\n",
-               pConfiguration->mpName);                
-       tas2557_load_data(pTAS2557, &(pConfiguration->mData), TAS2557_BLOCK_CFG_PRE_DEV_A);
+       nResult = tas2557_get_DAC_gain(pTAS2557, &nGain);
+       if (nResult < 0)
+               goto end;
+       pTAS2557->mnDevGain = nGain;
+       pTAS2557->mnDevCurrentGain = nGain;
 
-       nResult = pTAS2557->read(pTAS2557, TAS2557_PLL_CLKIN_REG, &Value);
-       dev_info(pTAS2557->dev, "TAS2557 PLL_CLKIN = 0x%02X\n", Value);
-       p_tas2557_startup_data[TAS2557_STARTUP_DATA_PLL_CLKIN_INDEX] = Value;
+       nResult = tas2557_load_coefficient(pTAS2557, -1, nConfiguration, false);
+       if (nResult < 0)
+               goto end;
 
-       tas2557_load_configuration(pTAS2557, nConfiguration, true);
-       if (pTAS2557->mbPowerUp){
+       tas2557_update_edge(pTAS2557);
+
+       if (pTAS2557->mbPowerUp) {
+               pTAS2557->clearIRQ(pTAS2557);
                dev_dbg(pTAS2557->dev, "device powered up, load startup\n");
-               tas2557_dev_load_data(pTAS2557, p_tas2557_startup_data);
-               dev_dbg(pTAS2557->dev,
-                       "device powered up, load unmute\n");
-               tas2557_dev_load_data(pTAS2557, p_tas2557_unmute_data);
+               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);
+                       }
+               }
        }
 
-       return 0;
+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)
+int tas2557_set_calibration(struct tas2557_priv *pTAS2557, int nCalibration)
 {
-       if ((!pTAS2557->mpFirmware->mpPrograms) || (!pTAS2557->mpFirmware->mpConfigurations)) 
-       {
+       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");
-               return -1;
+               nResult = 0;
+               goto end;
        }
 
-       if (nCalibration == 0x00FF)
-       {
-               dev_info(pTAS2557->dev, "load new calibration file %s\n", TAS2557_CAL_NAME);    
-               tas2557_load_calibration(pTAS2557, TAS2557_CAL_NAME);
+       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->mpFirmware->mnCalibrations) {
+       if (nCalibration >= pTAS2557->mpCalFirmware->mnCalibrations) {
                dev_err(pTAS2557->dev,
                        "Calibration %d doesn't exist\n", nCalibration);
-               return -1;
+               nResult = 0;
+               goto end;
        }
 
        pTAS2557->mnCurrentCalibration = nCalibration;
-       if(pTAS2557->mbPowerUp){
-               tas2557_load_block(pTAS2557, 
-                       &(pTAS2557->mpCalFirmware->mpCalibrations[pTAS2557->mnCurrentCalibration].mBlock));
-               pTAS2557->mbLoadCalibrationPostPowerUp = false; 
-       }else{
-               pTAS2557->mbLoadCalibrationPostPowerUp = true; 
+       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;
        }
 
-       return 0;
+end:
+       if (nResult < 0) {
+               tas2557_clear_firmware(pTAS2557->mpCalFirmware);
+               nResult = tas2557_set_program(pTAS2557, pTAS2557->mnCurrentProgram, pTAS2557->mnCurrentConfiguration);
+       }
+
+       return nResult;
 }
 
-int tas2557_parse_dt(struct device *dev,
-                       struct tas2557_priv *pTAS2557)
+bool tas2557_get_Cali_prm_r0(struct tas2557_priv *pTAS2557, int *prm_r0)
 {
-       struct device_node *np = dev->of_node;
-       int rc= 0, ret = 0;
+       struct TCalibration *pCalibration;
+       struct TData *pData;
+       int nReg;
+       int nCali_Re;
+       bool bFound = false;
+       int nBlockType;
 
-       rc = of_property_read_u32(np, "ti,load", &pTAS2557->mnLoad);
-       if (rc) {
-               dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n",
-                       "ti,load", np->full_name, rc);
-               ret = -1;
-       }else{
-               dev_dbg(pTAS2557->dev, "ti,load=%d", pTAS2557->mnLoad);
+       if (!pTAS2557->mpCalFirmware->mnCalibrations) {
+               dev_err(pTAS2557->dev, "%s, no calibration data\n", __func__);
+               goto end;
        }
 
-       rc = of_property_read_u32(np, "ti,channel", &pTAS2557->mnDevChl);
-       if (rc) {
-               dev_err(pTAS2557->dev, "Looking up %s property in node %s failed %d\n",
-                       "ti,channel", np->full_name, rc);
-               ret = -1;
-       }else{
-               dev_dbg(pTAS2557->dev, "ti,channel=%d", pTAS2557->mnDevChl);
-       }
-       
-       pTAS2557->mnResetGPIO = of_get_named_gpio(np,
-                               "ti,cdc-reset-gpio", 0);
-       if (pTAS2557->mnResetGPIO < 0) {
+       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 = -1;
-       }else{
-               dev_dbg(pTAS2557->dev, "ti,cdc-reset-gpio=%d", 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("GPLv2");
\ No newline at end of file
+MODULE_LICENSE("GPL v2");