First commit for TAS2560 Android device driver
authorTracy Yi <tracy-yi@ti.com>
Thu, 20 Dec 2018 09:27:19 +0000 (17:27 +0800)
committerTracy Yi <tracy-yi@ti.com>
Thu, 20 Dec 2018 09:27:19 +0000 (17:27 +0800)
Signed-off-by: Tracy Yi <tracy-yi@ti.com>
Kconfig [new file with mode: 0755]
Makefile [new file with mode: 0755]
tas2563-codec.c [new file with mode: 0644]
tas2563-codec.h [new file with mode: 0644]
tas2563-misc.c [new file with mode: 0755]
tas2563-misc.h [new file with mode: 0755]
tas2563-regmap.c [new file with mode: 0755]
tas2563.h [new file with mode: 0644]
tiload.c [new file with mode: 0755]
tiload.h [new file with mode: 0755]

diff --git a/Kconfig b/Kconfig
new file mode 100755 (executable)
index 0000000..86ab270
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,15 @@
+
+menuconfig SND_SOC_TAS2563
+       tristate "Texas Instruments TAS2563 SmartAmp(R)"
+
+if SND_SOC_TAS2563
+config TAS2563_REGMAP
+    bool "Use of RegMap API"
+
+config TAS2563_CODEC
+    bool "Codec Driver support"
+
+config TAS2563_MISC
+    bool "Misc Driver support"
+       
+endif # SND_SOC_TAS2563
diff --git a/Makefile b/Makefile
new file mode 100755 (executable)
index 0000000..05bcae1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,2 @@
+snd-soc-tas2563-objs := tas2563-regmap.o tas2563-codec.o tas2563-misc.o tiload.o
+obj-$(CONFIG_SND_SOC_TAS2563) += snd-soc-tas2563.o
diff --git a/tas2563-codec.c b/tas2563-codec.c
new file mode 100644 (file)
index 0000000..0a1b6a9
--- /dev/null
@@ -0,0 +1,2569 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** This program is free software; you can redistribute it and/or modify it under
+** the terms of the GNU General Public License as published by the Free Software
+** Foundation; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details.
+**
+** File:
+**     tas2563-codec.c
+**
+** Description:
+**     ALSA SoC driver for Texas Instruments TAS2563 High Performance 4W Smart
+**     Amplifier
+**
+** =============================================================================
+*/
+
+#ifdef CONFIG_TAS2563_CODEC
+#define DEBUG
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/fcntl.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/crc8.h>
+
+#include "tas2563.h"
+#include "tas2563-codec.h"
+
+#define        PPC_DRIVER_CRCCHK                       0x00000200
+#define        PPC_DRIVER_CONFDEV                      0x00000000
+#define        PPC_DRIVER_MTPLLSRC                     0x00000400
+#define        PPC_DRIVER_CFGDEV_NONCRC        0x00000101
+
+#define TAS2563_CAL_NAME    "/data/vendor/pa_cal/tas2563_cal.bin"
+#define RESTART_MAX 3
+
+#define TAS2563_UDELAY 0xFFFFFFFE
+#define TAS2563_MDELAY 0xFFFFFFFE
+#define KCONTROL_CODEC
+
+#define TAS2563_BLOCK_PLL                              0x00
+#define TAS2563_BLOCK_PGM_ALL                  0x0d
+#define TAS2563_BLOCK_PGM_DEV_A                        0x01
+#define TAS2563_BLOCK_PGM_DEV_B                        0x08
+#define TAS2563_BLOCK_CFG_COEFF_DEV_A  0x03
+#define TAS2563_BLOCK_CFG_COEFF_DEV_B  0x0a
+#define TAS2563_BLOCK_CFG_PRE_DEV_A            0x04
+#define TAS2563_BLOCK_CFG_PRE_DEV_B            0x0b
+#define TAS2563_BLOCK_CFG_POST                 0x05
+#define TAS2563_BLOCK_CFG_POST_POWER   0x06
+
+static char pICN[] = {0x00, 0x03, 0x46, 0xdc};
+static char const *iv_enable_text[] = {"Off", "On"};
+static int tas2563iv_enable;
+static const struct soc_enum tas2563_enum[] = {
+    SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(iv_enable_text), iv_enable_text),
+};
+static int tas2563_set_fmt(struct tas2563_priv *pTAS2563, unsigned int fmt);
+static void tas2563_clear_firmware(struct TFirmware *pFirmware);
+
+static int fw_parse(struct tas2563_priv *pTAS2563,
+       struct TFirmware *pFirmware, unsigned char *pData, unsigned int nSize);
+static bool tas2563_get_coefficient_in_block(struct tas2563_priv *pTAS2563,
+       struct TBlock *pBlock, int nReg, int *pnValue);
+int tas2563_set_program(struct tas2563_priv *pTAS2563, unsigned int nProgram, int nConfig);
+static int tas2563_set_calibration(struct tas2563_priv *pTAS2563, int nCalibration);
+static int tas2563_load_configuration(struct tas2563_priv *pTAS2563,
+       unsigned int nConfiguration, bool bLoadSame);
+static int tas2563_load_coefficient(struct tas2563_priv *pTAS2563,
+       int nPrevConfig, int nNewConfig, bool bPowerOn);
+
+static unsigned int tas2563_codec_read(struct snd_soc_codec *codec,
+               unsigned int reg)
+{
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+       int nResult = 0;
+       unsigned int value = 0;
+
+       nResult = pTAS2563->read(pTAS2563, reg, &value);
+
+       if (nResult < 0)
+               dev_err(pTAS2563->dev, "%s, ERROR, reg=0x%x, E=%d\n",
+                       __func__, reg, nResult);
+       else
+               dev_info(pTAS2563->dev, "%s, reg: 0x%x, value: 0x%x\n",
+                               __func__, reg, value);
+
+       if (nResult >= 0)
+               return value;
+       else
+               return nResult;
+}
+
+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 tas2563_priv *pTAS2563, struct TYCRC *pCRCData,
+       unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len)
+{
+       int nResult = 0;
+
+       if (nBook == TAS2563_YRAM_BOOK1) {
+               if (nPage == TAS2563_YRAM1_PAGE) {
+                       if (nReg >= TAS2563_YRAM1_START_REG) {
+                               pCRCData->mnOffset = nReg;
+                               pCRCData->mnLen = len;
+                               nResult = 1;
+                       } else if ((nReg + len) > TAS2563_YRAM1_START_REG) {
+                               pCRCData->mnOffset = TAS2563_YRAM1_START_REG;
+                               pCRCData->mnLen = len - (TAS2563_YRAM1_START_REG - nReg);
+                               nResult = 1;
+                       } else
+                               nResult = 0;
+               } else if (nPage == TAS2563_YRAM3_PAGE) {
+                       if (nReg > TAS2563_YRAM3_END_REG) {
+                               nResult = 0;
+                       } else if (nReg >= TAS2563_YRAM3_START_REG) {
+                               if ((nReg + len) > TAS2563_YRAM3_END_REG) {
+                                       pCRCData->mnOffset = nReg;
+                                       pCRCData->mnLen = TAS2563_YRAM3_END_REG - nReg + 1;
+                                       nResult = 1;
+                               } else {
+                                       pCRCData->mnOffset = nReg;
+                                       pCRCData->mnLen = len;
+                                       nResult = 1;
+                               }
+                       } else {
+                               if ((nReg + (len - 1)) < TAS2563_YRAM3_START_REG)
+                                       nResult = 0;
+                               else {
+                                       pCRCData->mnOffset = TAS2563_YRAM3_START_REG;
+                                       pCRCData->mnLen = len - (TAS2563_YRAM3_START_REG - nReg);
+                                       nResult = 1;
+                               }
+                       }
+               }
+       } else if (nBook == TAS2563_YRAM_BOOK2) {
+               if (nPage == TAS2563_YRAM5_PAGE) {
+                       if (nReg > TAS2563_YRAM5_END_REG) {
+                               nResult = 0;
+                       } else if (nReg >= TAS2563_YRAM5_START_REG) {
+                               if ((nReg + len) > TAS2563_YRAM5_END_REG) {
+                                       pCRCData->mnOffset = nReg;
+                                       pCRCData->mnLen = TAS2563_YRAM5_END_REG - nReg + 1;
+                                       nResult = 1;
+                               } else {
+                                       pCRCData->mnOffset = nReg;
+                                       pCRCData->mnLen = len;
+                                       nResult = 1;
+                               }
+                       } else {
+                               if ((nReg + (len - 1)) < TAS2563_YRAM5_START_REG)
+                                       nResult = 0;
+                               else {
+                                       pCRCData->mnOffset = TAS2563_YRAM5_START_REG;
+                                       pCRCData->mnLen = len - (TAS2563_YRAM5_START_REG - nReg);
+                                       nResult = 1;
+                               }
+                       }
+               }
+       } else
+               nResult = 0;
+
+       return nResult;
+}
+
+static int isInBlockYRAM(struct tas2563_priv *pTAS2563, struct TYCRC *pCRCData,
+       unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len)
+{
+       int nResult;
+
+       if (nBook == TAS2563_YRAM_BOOK1) {
+               if (nPage < TAS2563_YRAM2_START_PAGE)
+                       nResult = 0;
+               else if (nPage <= TAS2563_YRAM2_END_PAGE) {
+                       if (nReg > TAS2563_YRAM2_END_REG)
+                               nResult = 0;
+                       else if (nReg >= TAS2563_YRAM2_START_REG) {
+                               pCRCData->mnOffset = nReg;
+                               pCRCData->mnLen = len;
+                               nResult = 1;
+                       } else {
+                               if ((nReg + (len - 1)) < TAS2563_YRAM2_START_REG)
+                                       nResult = 0;
+                               else {
+                                       pCRCData->mnOffset = TAS2563_YRAM2_START_REG;
+                                       pCRCData->mnLen = nReg + len - TAS2563_YRAM2_START_REG;
+                                       nResult = 1;
+                               }
+                       }
+               } else
+                       nResult = 0;
+       } else if (nBook == TAS2563_YRAM_BOOK2) {
+               if (nPage < TAS2563_YRAM4_START_PAGE)
+                       nResult = 0;
+               else if (nPage <= TAS2563_YRAM4_END_PAGE) {
+                       if (nReg > TAS2563_YRAM2_END_REG)
+                               nResult = 0;
+                       else if (nReg >= TAS2563_YRAM2_START_REG) {
+                               pCRCData->mnOffset = nReg;
+                               pCRCData->mnLen = len;
+                               nResult = 1;
+                       } else {
+                               if ((nReg + (len - 1)) < TAS2563_YRAM2_START_REG)
+                                       nResult = 0;
+                               else {
+                                       pCRCData->mnOffset = TAS2563_YRAM2_START_REG;
+                                       pCRCData->mnLen = nReg + len - TAS2563_YRAM2_START_REG;
+                                       nResult = 1;
+                               }
+                       }
+               } else
+                       nResult = 0;
+       } else
+               nResult = 0;
+
+       return nResult;
+}
+
+
+static int isYRAM(struct tas2563_priv *pTAS2563, struct TYCRC *pCRCData,
+       unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char len)
+{
+       int nResult;
+
+       nResult = isInPageYRAM(pTAS2563, pCRCData, nBook, nPage, nReg, len);
+
+       if (nResult == 0)
+               nResult = isInBlockYRAM(pTAS2563, 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 tas2563_priv *pTAS2563, 
+       unsigned char nBook, unsigned char nPage, unsigned char nReg, unsigned char nValue)
+{
+       int nResult = 0;
+       struct TYCRC sCRCData;
+       unsigned int nData1 = 0;
+
+       if ((nBook == TAS2563_BOOK_ID(TAS2563_SA_COEFF_SWAP_REG))
+               && (nPage == TAS2563_PAGE_ID(TAS2563_SA_COEFF_SWAP_REG))
+               && (nReg >= TAS2563_PAGE_REG(TAS2563_SA_COEFF_SWAP_REG))
+               && (nReg <= (TAS2563_PAGE_REG(TAS2563_SA_COEFF_SWAP_REG) + 4))) {
+               /* DSP swap command, pass */
+               nResult = 0;
+               goto end;
+       }
+
+       nResult = isYRAM(pTAS2563, &sCRCData, nBook, nPage, nReg, 1);
+       if (nResult == 1) {
+               nResult = pTAS2563->read(pTAS2563, TAS2563_REG(nBook, nPage, nReg), &nData1);
+               if (nResult < 0)
+                       goto end;
+
+               if (nData1 != nValue) {
+                       dev_err(pTAS2563->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 tas2563_priv *pTAS2563, 
+       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;
+       
+       return 0;
+
+       if ((nReg + len-1) > 127) {
+               nResult = -EINVAL;
+               dev_err(pTAS2563->dev, "firmware error\n");
+               goto end;
+       }
+
+       if ((nBook == TAS2563_BOOK_ID(TAS2563_SA_COEFF_SWAP_REG))
+               && (nPage == TAS2563_PAGE_ID(TAS2563_SA_COEFF_SWAP_REG))
+               && (nReg == TAS2563_PAGE_REG(TAS2563_SA_COEFF_SWAP_REG))
+               && (len == 4)) {
+               /* DSP swap command, pass */
+               nResult = 0;
+               goto end;
+       }
+
+       nResult = isYRAM(pTAS2563, &TCRCData, nBook, nPage, nReg, len);
+       dev_info(pTAS2563->dev, "isYRAM: nBook 0x%x, nPage 0x%x, nReg 0x%x\n", nBook, nPage, nReg);
+       dev_info(pTAS2563->dev, "isYRAM: TCRCData.mnLen 0x%x, len 0x%x, nResult %d\n", TCRCData.mnLen, len, nResult);
+       dev_info(pTAS2563->dev, "TCRCData.mnOffset %x\n", TCRCData.mnOffset);
+       if (nResult == 1) {
+               if (len == 1) {
+                       dev_err(pTAS2563->dev, "firmware error\n");
+                       nResult = -EINVAL;
+                       goto end;
+               } else {
+                       nResult = pTAS2563->bulk_read(pTAS2563, TAS2563_REG(nBook, nPage, TCRCData.mnOffset), nBuf1, TCRCData.mnLen);
+                       if (nResult < 0)
+                               goto end;
+
+                       for (i = 0; i < TCRCData.mnLen; i++) {
+                               if ((nBook == TAS2563_BOOK_ID(TAS2563_SA_COEFF_SWAP_REG))
+                                       && (nPage == TAS2563_PAGE_ID(TAS2563_SA_COEFF_SWAP_REG))
+                                       && ((i + TCRCData.mnOffset)
+                                               >= TAS2563_PAGE_REG(TAS2563_SA_COEFF_SWAP_REG))
+                                       && ((i + TCRCData.mnOffset)
+                                               <= (TAS2563_PAGE_REG(TAS2563_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 tas2563_load_block(struct tas2563_priv *pTAS2563, 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 nValue;
+       int nRetry = 6;
+       unsigned char *pData = pBlock->mpData;
+
+       dev_info(pTAS2563->dev, "TAS2563 load block: Type = %d, commands = %d\n",
+               pBlock->mnType, pBlock->mnCommands);
+start:
+       if (pBlock->mbPChkSumPresent) {
+               nResult = pTAS2563->write(pTAS2563, TAS2563_I2CChecksum, 0);
+               if (nResult < 0)
+                       goto end;
+       }
+
+       if (pBlock->mbYChkSumPresent)
+               nCRCChkSum = 0;
+
+       nCommand = 0;
+
+       while (nCommand < pBlock->mnCommands) {
+               pData = pBlock->mpData + nCommand * 4;
+
+               nBook = pData[0];
+               nPage = pData[1];
+               nOffset = pData[2];
+               nData = pData[3];
+
+               nCommand++;
+
+               if (nOffset <= 0x7F) {
+                       nResult = pTAS2563->write(pTAS2563, TAS2563_REG(nBook, nPage, nOffset), nData);
+                       if (nResult < 0)
+                               goto end;
+                       if (pBlock->mbYChkSumPresent) {
+                               nResult = doSingleRegCheckSum(pTAS2563, nBook, nPage, nOffset, nData);
+                               if (nResult < 0)
+                                       goto check;
+                               nCRCChkSum += (unsigned char)nResult;
+                       }
+               } else if (nOffset == 0x81) {
+                       nSleep = (nBook << 8) + nPage;
+                       msleep(nSleep);
+               } else if (nOffset == 0x85) {
+                       pData += 4;
+                       nLength = (nBook << 8) + nPage;
+                       nBook = pData[0];
+                       nPage = pData[1];
+                       nOffset = pData[2];
+                       if (nLength > 1) {
+                               nResult = pTAS2563->bulk_write(pTAS2563, TAS2563_REG(nBook, nPage, nOffset), pData + 3, nLength);
+                               if (nResult < 0)
+                                       goto end;
+                               if (pBlock->mbYChkSumPresent) {
+                                       nResult = doMultiRegCheckSum(pTAS2563, nBook, nPage, nOffset, nLength);
+                                       if (nResult < 0)
+                                               goto check;
+                                       nCRCChkSum += (unsigned char)nResult;
+                               }
+                       } else {
+                               nResult = pTAS2563->write(pTAS2563, TAS2563_REG(nBook, nPage, nOffset), pData[3]);
+                               if (nResult < 0)
+                                       goto end;
+                               if (pBlock->mbYChkSumPresent) {
+                                       nResult = doSingleRegCheckSum(pTAS2563, 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 = pTAS2563->read(pTAS2563, TAS2563_I2CChecksum, &nValue);
+                dev_err(pTAS2563->dev, "Block PChkSum: FW = 0x%x, Reg = 0x%x\n",
+                               pBlock->mnPChkSum, (nValue&0xff));
+
+               if (nResult < 0)
+                       goto end;
+               if ((nValue&0xff) != pBlock->mnPChkSum) {
+                       dev_err(pTAS2563->dev, "Block PChkSum Error: FW = 0x%x, Reg = 0x%x\n",
+                               pBlock->mnPChkSum, (nValue&0xff));
+                       nResult = -EAGAIN;
+                               pTAS2563->mnErrCode |= ERROR_PRAM_CRCCHK;
+                       goto check;
+               }
+
+               nResult = 0;
+               pTAS2563->mnErrCode &= ~ERROR_PRAM_CRCCHK;
+               dev_info(pTAS2563->dev, "Block[0x%x] PChkSum match\n", pBlock->mnType);
+       }
+
+       if (pBlock->mbYChkSumPresent) {
+               //TBD, open it when FW ready
+                dev_err(pTAS2563->dev, "Block YChkSum: FW = 0x%x, YCRC = 0x%x\n",
+                               pBlock->mnYChkSum, nCRCChkSum);
+/*
+               if (nCRCChkSum != pBlock->mnYChkSum) {
+                       dev_err(pTAS2563->dev, "Block YChkSum Error: FW = 0x%x, YCRC = 0x%x\n",
+                               pBlock->mnYChkSum, nCRCChkSum);
+                       nResult = -EAGAIN;
+                       pTAS2563->mnErrCode |= ERROR_YRAM_CRCCHK;
+                       goto check;
+               }
+*/
+               pTAS2563->mnErrCode &= ~ERROR_YRAM_CRCCHK;
+               nResult = 0;
+               dev_info(pTAS2563->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(pTAS2563->dev, "Block (%d) load error\n",
+                               pBlock->mnType);
+       }
+       return nResult;
+}
+
+
+static int tas2563_load_data(struct tas2563_priv *pTAS2563, struct TData *pData, unsigned int nType)
+{
+       int nResult = 0;
+       unsigned int nBlock;
+       struct TBlock *pBlock;
+
+       dev_info(pTAS2563->dev,
+               "TAS2563 load data: %s, Blocks = %d, Block Type = %d\n", pData->mpName, pData->mnBlocks, nType);
+
+       for (nBlock = 0; nBlock < pData->mnBlocks; nBlock++) {
+               pBlock = &(pData->mpBlocks[nBlock]);
+               if (pBlock->mnType == nType) {
+                       nResult = tas2563_load_block(pTAS2563, pBlock);
+                       if (nResult < 0)
+                               break;
+               }
+       }
+
+       return nResult;
+}
+
+void tas2563_clear_firmware(struct TFirmware *pFirmware)
+{
+       unsigned int n, nn;
+
+       if (!pFirmware)
+               return;
+
+       kfree(pFirmware->mpDescription);
+
+       if (pFirmware->mpPLLs != NULL) {
+               for (n = 0; n < pFirmware->mnPLLs; n++) {
+                       kfree(pFirmware->mpPLLs[n].mpDescription);
+                       kfree(pFirmware->mpPLLs[n].mBlock.mpData);
+               }
+               kfree(pFirmware->mpPLLs);
+       }
+
+       if (pFirmware->mpPrograms != NULL) {
+               for (n = 0; n < pFirmware->mnPrograms; n++) {
+                       kfree(pFirmware->mpPrograms[n].mpDescription);
+                       kfree(pFirmware->mpPrograms[n].mData.mpDescription);
+                       for (nn = 0; nn < pFirmware->mpPrograms[n].mData.mnBlocks; nn++)
+                               kfree(pFirmware->mpPrograms[n].mData.mpBlocks[nn].mpData);
+                       kfree(pFirmware->mpPrograms[n].mData.mpBlocks);
+               }
+               kfree(pFirmware->mpPrograms);
+       }
+
+       if (pFirmware->mpConfigurations != NULL) {
+               for (n = 0; n < pFirmware->mnConfigurations; n++) {
+                       kfree(pFirmware->mpConfigurations[n].mpDescription);
+                       kfree(pFirmware->mpConfigurations[n].mData.mpDescription);
+                       for (nn = 0; nn < pFirmware->mpConfigurations[n].mData.mnBlocks; nn++)
+                               kfree(pFirmware->mpConfigurations[n].mData.mpBlocks[nn].mpData);
+                       kfree(pFirmware->mpConfigurations[n].mData.mpBlocks);
+               }
+               kfree(pFirmware->mpConfigurations);
+       }
+
+       if (pFirmware->mpCalibrations != NULL) {
+               for (n = 0; n < pFirmware->mnCalibrations; n++) {
+                       kfree(pFirmware->mpCalibrations[n].mpDescription);
+                       kfree(pFirmware->mpCalibrations[n].mData.mpDescription);
+                       for (nn = 0; nn < pFirmware->mpCalibrations[n].mData.mnBlocks; nn++)
+                               kfree(pFirmware->mpCalibrations[n].mData.mpBlocks[nn].mpData);
+                       kfree(pFirmware->mpCalibrations[n].mData.mpBlocks);
+               }
+               kfree(pFirmware->mpCalibrations);
+       }
+
+       memset(pFirmware, 0x00, sizeof(struct TFirmware));
+}
+
+static int tas2563_load_configuration(struct tas2563_priv *pTAS2563,
+       unsigned int nConfiguration, bool bLoadSame)
+{
+       int nResult = 0;
+       struct TConfiguration *pCurrentConfiguration = NULL;
+       struct TConfiguration *pNewConfiguration = NULL;
+
+       dev_info(pTAS2563->dev, "%s: %d\n", __func__, nConfiguration);
+
+       if ((!pTAS2563->mpFirmware->mpPrograms) ||
+               (!pTAS2563->mpFirmware->mpConfigurations)) {
+               dev_err(pTAS2563->dev, "%s, Firmware not loaded\n", __func__);
+               nResult = 0;
+               goto end;
+       }
+
+       if (nConfiguration >= pTAS2563->mpFirmware->mnConfigurations) {
+               dev_err(pTAS2563->dev, "Configuration %d doesn't exist\n",
+                       nConfiguration);
+               nResult = 0;
+               goto end;
+       }
+
+       if ((!pTAS2563->mbLoadConfigurationPrePowerUp)
+               && (nConfiguration == pTAS2563->mnCurrentConfiguration)
+               && (!bLoadSame)) {
+               dev_info(pTAS2563->dev, "Configuration %d is already loaded\n",
+                       nConfiguration);
+               nResult = 0;
+               goto end;
+       }
+
+       pCurrentConfiguration =
+               &(pTAS2563->mpFirmware->mpConfigurations[pTAS2563->mnCurrentConfiguration]);
+       pNewConfiguration =
+               &(pTAS2563->mpFirmware->mpConfigurations[nConfiguration]);
+       if (pNewConfiguration->mnProgram != pCurrentConfiguration->mnProgram) {
+               dev_err(pTAS2563->dev, "Configuration %d, %s doesn't share the same program as current %d\n",
+                       nConfiguration, pNewConfiguration->mpName, pCurrentConfiguration->mnProgram);
+               nResult = 0;
+               goto end;
+       }
+
+       if (pTAS2563->mbPowerUp) {
+               pTAS2563->mbLoadConfigurationPrePowerUp = false;
+               nResult = tas2563_load_coefficient(pTAS2563, pTAS2563->mnCurrentConfiguration, nConfiguration, true);
+       } else {
+               dev_info(pTAS2563->dev,
+                       "TAS2563 was powered down, will load coefficient when power up\n");
+               pTAS2563->mbLoadConfigurationPrePowerUp = true;
+               pTAS2563->mnNewConfiguration = nConfiguration;
+       }
+
+end:
+
+/*     if (nResult < 0) {
+               if (pTAS2563->mnErrCode & (ERROR_DEVA_I2C_COMM | ERROR_PRAM_CRCCHK | ERROR_YRAM_CRCCHK))
+                       failsafe(pTAS2563);
+       }
+*/
+       return nResult;
+}
+
+static int tas2563_load_calibration(struct tas2563_priv *pTAS2563,     char *pFileName)
+{
+       int nResult = 0;
+
+       int nFile;
+       mm_segment_t fs;
+       unsigned char pBuffer[1000];
+       int nSize = 0;
+
+       dev_info(pTAS2563->dev, "%s:\n", __func__);
+
+       fs = get_fs();
+       set_fs(KERNEL_DS);
+       nFile = sys_open(pFileName, O_RDONLY, 0);
+
+       dev_info(pTAS2563->dev, "TAS2563 calibration file = %s, handle = %d\n",
+               pFileName, nFile);
+
+       if (nFile >= 0) {
+               nSize = sys_read(nFile, pBuffer, 1000);
+               sys_close(nFile);
+       } else {
+               dev_err(pTAS2563->dev, "TAS2563 cannot open calibration file: %s\n",
+                       pFileName);
+       }
+
+       set_fs(fs);
+
+       if (!nSize)
+               goto end;
+
+       tas2563_clear_firmware(pTAS2563->mpCalFirmware);
+       dev_info(pTAS2563->dev, "TAS2563 calibration file size = %d\n", nSize);
+       nResult = fw_parse(pTAS2563, pTAS2563->mpCalFirmware, pBuffer, nSize);
+
+       if (nResult)
+               dev_err(pTAS2563->dev, "TAS2563 calibration file is corrupt\n");
+       else
+               dev_info(pTAS2563->dev, "TAS2563 calibration: %d calibrations\n",
+                       pTAS2563->mpCalFirmware->mnCalibrations);
+end:
+
+       return nResult;
+}
+
+
+static int tas2563iv_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+#endif
+
+    if (codec == NULL) {
+               pr_err("%s: codec is NULL \n",  __func__);
+               return 0;
+    }
+
+    tas2563iv_enable = ucontrol->value.integer.value[0];
+
+       if (tas2563iv_enable) {
+               pr_debug("%s: tas2563iv_enable \n", __func__);
+               snd_soc_update_bits(codec, TAS2563_PowerControl,
+                       TAS2563_PowerControl_OperationalMode10_Mask |
+                   TAS2563_PowerControl_ISNSPower_Mask |
+                   TAS2563_PowerControl_VSNSPower_Mask,
+                   TAS2563_PowerControl_OperationalMode10_Active |
+                   TAS2563_PowerControl_VSNSPower_Active |
+                   TAS2563_PowerControl_ISNSPower_Active);
+       } else {
+               pr_debug("%s: tas2563iv_disable \n", __func__);
+               snd_soc_update_bits(codec, TAS2563_PowerControl,
+                       TAS2563_PowerControl_OperationalMode10_Mask |
+                       TAS2563_PowerControl_ISNSPower_Mask |
+                       TAS2563_PowerControl_VSNSPower_Mask,
+                       TAS2563_PowerControl_OperationalMode10_Active |
+                       TAS2563_PowerControl_VSNSPower_PoweredDown |
+                       TAS2563_PowerControl_ISNSPower_PoweredDown);
+       }
+
+       pr_debug("%s: tas2563iv_enable = %d\n", __func__, tas2563iv_enable);
+
+       return 0;
+}
+
+static int tas2563iv_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+   int value;
+   ucontrol->value.integer.value[0] = tas2563iv_enable;
+   value=gpio_get_value(37);
+   pr_debug("%s: tas2563iv_enable = %d\n", __func__, tas2563iv_enable);
+   pr_debug("%s: gpio37 value = %d\n", __func__, value);
+   return 0;
+}
+
+static const struct snd_kcontrol_new tas2563_controls[] = {
+SOC_ENUM_EXT("TAS2563 IVSENSE ENABLE", tas2563_enum[1],
+                   tas2563iv_get, tas2563iv_put),
+};
+
+static int tas2563_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+       unsigned int value)
+{
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+
+       int nResult = 0;
+
+       nResult = pTAS2563->write(pTAS2563, reg, value);
+       if (nResult < 0)
+               dev_err(pTAS2563->dev, "%s, ERROR, reg=0x%x, E=%d\n",
+                       __func__, reg, nResult);
+       else
+               dev_info(pTAS2563->dev, "%s, reg: 0x%x, 0x%x\n",
+                       __func__, reg, value);
+
+       return nResult;
+
+}
+
+static void fw_print_header(struct tas2563_priv *pTAS2563, struct TFirmware *pFirmware)
+{
+       dev_info(pTAS2563->dev, "FW Size       = %d", pFirmware->mnFWSize);
+       dev_info(pTAS2563->dev, "Checksum      = 0x%04X", pFirmware->mnChecksum);
+       dev_info(pTAS2563->dev, "PPC Version   = 0x%04X", pFirmware->mnPPCVersion);
+       dev_info(pTAS2563->dev, "FW  Version    = 0x%04X", pFirmware->mnFWVersion);
+       dev_info(pTAS2563->dev, "Driver Version= 0x%04X", pFirmware->mnDriverVersion);
+       dev_info(pTAS2563->dev, "Timestamp     = %d", pFirmware->mnTimeStamp);
+       dev_info(pTAS2563->dev, "DDC Name      = %s", pFirmware->mpDDCName);
+       dev_info(pTAS2563->dev, "Description   = %s", pFirmware->mpDescription);
+}
+
+inline unsigned int fw_convert_number(unsigned char *pData)
+{
+       return pData[3] + (pData[2] << 8) + (pData[1] << 16) + (pData[0] << 24);
+}
+
+static int fw_parse_header(struct tas2563_priv *pTAS2563,
+       struct TFirmware *pFirmware, unsigned char *pData, unsigned int nSize)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int n;
+       unsigned char pMagicNumber[] = { 0x35, 0x35, 0x35, 0x32 };
+
+       if (nSize < 104) {
+               dev_err(pTAS2563->dev, "Firmware: Header too short");
+               return -EINVAL;
+       }
+
+       if (memcmp(pData, pMagicNumber, 4)) {
+               dev_err(pTAS2563->dev, "Firmware: Magic number doesn't match");
+               return -EINVAL;
+       }
+       pData += 4;
+
+       pFirmware->mnFWSize = fw_convert_number(pData);
+       pData += 4;
+       dev_info(pTAS2563->dev, "firmware size: %d", pFirmware->mnFWSize);
+
+       pFirmware->mnChecksum = fw_convert_number(pData);
+       pData += 4;
+
+       pFirmware->mnPPCVersion = fw_convert_number(pData);
+       pData += 4;
+
+       pFirmware->mnFWVersion = fw_convert_number(pData);
+       pData += 4;
+
+       pFirmware->mnDriverVersion = fw_convert_number(pData);
+       pData += 4;
+
+       pFirmware->mnTimeStamp = fw_convert_number(pData);
+       pData += 4;
+       dev_info(pTAS2563->dev, "FW timestamp: %d", pFirmware->mnTimeStamp);
+
+       memcpy(pFirmware->mpDDCName, pData, 64);
+       pData += 64;
+
+       n = strlen(pData);
+       pFirmware->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+       pData += n + 1;
+       if ((pData - pDataStart) >= nSize) {
+               dev_err(pTAS2563->dev, "Firmware: Header too short after DDC description");
+               return -EINVAL;
+       }
+
+       pFirmware->mnDeviceFamily = fw_convert_number(pData);
+       pData += 4;
+       if (pFirmware->mnDeviceFamily != 0) {
+               dev_err(pTAS2563->dev,
+                       "deviceFamily %d, not TAS device", pFirmware->mnDeviceFamily);
+               return -EINVAL;
+       }
+
+       pFirmware->mnDevice = fw_convert_number(pData);
+       pData += 4;
+
+       if (pFirmware->mnDevice != 5) {
+               dev_err(pTAS2563->dev,
+                       "device %d, not TAS2563", pFirmware->mnDevice);
+               return -EINVAL;
+       }
+
+       fw_print_header(pTAS2563, pFirmware);
+       return pData - pDataStart;
+}
+
+static int fw_parse_block_data(struct tas2563_priv *pTAS2563, struct TFirmware *pFirmware,
+       struct TBlock *pBlock, unsigned char *pData)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int n;
+
+       dev_info(pTAS2563->dev, "%s, %d", __func__, __LINE__);
+
+       pBlock->mnType = fw_convert_number(pData);
+       pData += 4;
+       dev_info(pTAS2563->dev, "%s, %d", __func__, __LINE__);
+
+       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;
+       dev_info(pTAS2563->dev, "%s, %d", __func__, __LINE__);
+       return pData - pDataStart;
+}
+
+static int fw_parse_data(struct tas2563_priv *pTAS2563, struct TFirmware *pFirmware,
+       struct TData *pImageData, unsigned char *pData)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int nBlock;
+       unsigned int n;
+
+       dev_info(pTAS2563->dev, "%s, %d", __func__, __LINE__);
+       memcpy(pImageData->mpName, pData, 64);
+       pData += 64;
+
+       n = strlen(pData);
+       pImageData->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+       pData += n + 1;
+
+       pImageData->mnBlocks = (pData[0] << 8) + pData[1];
+       pData += 2;
+
+       pImageData->mpBlocks =
+               kmalloc(sizeof(struct TBlock) * pImageData->mnBlocks, GFP_KERNEL);
+
+       for (nBlock = 0; nBlock < pImageData->mnBlocks; nBlock++) {
+               n = fw_parse_block_data(pTAS2563, pFirmware,
+                       &(pImageData->mpBlocks[nBlock]), pData);
+               pData += n;
+       }
+       return pData - pDataStart;
+}
+
+#if 0
+static int fw_parse_pll_data(struct tas2563_priv *pTAS2563,
+       struct TFirmware *pFirmware, unsigned char *pData)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int n;
+       unsigned int nPLL;
+       struct TPLL *pPLL;
+
+       pFirmware->mnPLLs = (pData[0] << 8) + pData[1];
+       pData += 2;
+
+       if (pFirmware->mnPLLs == 0)
+               goto end;
+
+       pFirmware->mpPLLs = kmalloc_array(pFirmware->mnPLLs, sizeof(struct TPLL), GFP_KERNEL);
+       for (nPLL = 0; nPLL < pFirmware->mnPLLs; nPLL++) {
+               pPLL = &(pFirmware->mpPLLs[nPLL]);
+
+               memcpy(pPLL->mpName, pData, 64);
+               pData += 64;
+
+               n = strlen(pData);
+               pPLL->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+               pData += n + 1;
+
+               n = fw_parse_block_data(pTAS2563, pFirmware, &(pPLL->mBlock), pData);
+               pData += n;
+       }
+
+end:
+       return pData - pDataStart;
+}
+#endif
+
+static int fw_parse_program_data(struct tas2563_priv *pTAS2563,
+       struct TFirmware *pFirmware, unsigned char *pData)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int n;
+       unsigned int nProgram;
+       struct TProgram *pProgram;
+
+       pFirmware->mnPrograms = (pData[0] << 8) + pData[1];
+       pData += 2;
+
+       if (pFirmware->mnPrograms == 0)
+               goto end;
+
+       pFirmware->mpPrograms =
+               kmalloc(sizeof(struct TProgram) * pFirmware->mnPrograms, GFP_KERNEL);
+       for (nProgram = 0; nProgram < pFirmware->mnPrograms; nProgram++) {
+               pProgram = &(pFirmware->mpPrograms[nProgram]);
+               memcpy(pProgram->mpName, pData, 64);
+               pData += 64;
+
+               n = strlen(pData);
+               pProgram->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+               pData += n + 1;
+
+               pProgram->mnAppMode = pData[0];
+               pData++;
+
+               /*pProgram->mnBoost = (pData[0] << 8) + pData[1];
+               pData += 2;*/
+               pProgram->mnI2sMode = pData[0];
+               pData++;
+               dev_info(pTAS2563->dev, "FW i2sMode: %d", pProgram->mnI2sMode);
+
+               pProgram->mnISnsPD = pData[0];
+               pData++;
+
+               pProgram->mnVSnsPD = pData[0];
+               pData++;
+
+               pProgram->mnPowerLDG = pData[0];
+               pData++;
+
+               n = fw_parse_data(pTAS2563, pFirmware, &(pProgram->mData), pData);
+               pData += n;
+               dev_info(pTAS2563->dev, "program data number: %d", n);
+       }
+
+end:
+
+       return pData - pDataStart;
+}
+
+static int fw_parse_configuration_data(struct tas2563_priv *pTAS2563,
+       struct TFirmware *pFirmware, unsigned char *pData)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int n;
+       unsigned int nConfiguration;
+       struct TConfiguration *pConfiguration;
+
+       pFirmware->mnConfigurations = (pData[0] << 8) + pData[1];
+       pData += 2;
+
+       if (pFirmware->mnConfigurations == 0)
+               goto end;
+
+       pFirmware->mpConfigurations =
+               kmalloc(sizeof(struct TConfiguration) * pFirmware->mnConfigurations,
+               GFP_KERNEL);
+       for (nConfiguration = 0; nConfiguration < pFirmware->mnConfigurations;
+               nConfiguration++) {
+               pConfiguration = &(pFirmware->mpConfigurations[nConfiguration]);
+               memcpy(pConfiguration->mpName, pData, 64);
+               pData += 64;
+
+               n = strlen(pData);
+               pConfiguration->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+               pData += n + 1;
+
+/*
+               if ((pFirmware->mnDriverVersion >= PPC_DRIVER_CONFDEV)
+                       || ((pFirmware->mnDriverVersion >= PPC_DRIVER_CFGDEV_NONCRC)
+                               && (pFirmware->mnDriverVersion < PPC_DRIVER_CRCCHK))) {*/
+                       pConfiguration->mnDevices = (pData[0] << 8) + pData[1];
+                       pData += 2;
+/*             } else
+                       pConfiguration->mnDevices = 1;*/
+
+               pConfiguration->mnProgram = pData[0];
+               pData++;
+               dev_info(pTAS2563->dev, "configuration, mnProgram: %d", pConfiguration->mnProgram);
+
+               pConfiguration->mnSamplingRate = fw_convert_number(pData);
+               pData += 4;
+               dev_info(pTAS2563->dev, "configuration samplerate: %d", pConfiguration->mnSamplingRate);
+
+               //if (pFirmware->mnDriverVersion >= PPC_DRIVER_MTPLLSRC) {
+                       pConfiguration->mnPLLSrc = pData[0];
+                       pData++;
+
+                       pConfiguration->mnPLLSrcRate = fw_convert_number(pData);
+                       pData += 4;
+               //}
+
+               pConfiguration->mnFsRate = (pData[0] << 8) + pData[1];
+               pData += 2;
+               dev_info(pTAS2563->dev, "Fs rate: %d", pConfiguration->mnFsRate);
+
+               n = fw_parse_data(pTAS2563, pFirmware, &(pConfiguration->mData), pData);
+               pData += n;
+       }
+
+end:
+
+       return pData - pDataStart;
+}
+
+int fw_parse_calibration_data(struct tas2563_priv *pTAS2563,
+       struct TFirmware *pFirmware, unsigned char *pData)
+{
+       unsigned char *pDataStart = pData;
+       unsigned int n;
+       unsigned int nCalibration;
+       struct TCalibration *pCalibration;
+
+       pFirmware->mnCalibrations = (pData[0] << 8) + pData[1];
+       pData += 2;
+
+       if (pFirmware->mnCalibrations == 0)
+               goto end;
+
+       pFirmware->mpCalibrations =
+               kmalloc(sizeof(struct TCalibration) * pFirmware->mnCalibrations, GFP_KERNEL);
+       for (nCalibration = 0;
+               nCalibration < pFirmware->mnCalibrations;
+               nCalibration++) {
+               pCalibration = &(pFirmware->mpCalibrations[nCalibration]);
+               memcpy(pCalibration->mpName, pData, 64);
+               pData += 64;
+
+               n = strlen(pData);
+               pCalibration->mpDescription = kmemdup(pData, n + 1, GFP_KERNEL);
+               pData += n + 1;
+
+               pCalibration->mnProgram = pData[0];
+               pData++;
+
+               pCalibration->mnConfiguration = pData[0];
+               pData++;
+
+               n = fw_parse_data(pTAS2563, pFirmware, &(pCalibration->mData), pData);
+               pData += n;
+       }
+
+end:
+
+       return pData - pDataStart;
+}
+
+static int fw_parse(struct tas2563_priv *pTAS2563,
+       struct TFirmware *pFirmware, unsigned char *pData, unsigned int nSize)
+{
+       int nPosition = 0;
+
+       nPosition = fw_parse_header(pTAS2563, pFirmware, pData, nSize);
+       dev_info(pTAS2563->dev, "header size: %d, line: %d\n", nPosition, __LINE__);
+       if (nPosition < 0) {
+               dev_err(pTAS2563->dev, "Firmware: Wrong Header");
+               return -EINVAL;
+       }
+
+       if (nPosition >= nSize) {
+               dev_err(pTAS2563->dev, "Firmware: Too short");
+               return -EINVAL;
+       }
+
+       pData += nPosition;
+       nSize -= nPosition;
+       nPosition = 0;
+
+       /*removed pll section*/
+/*     nPosition = fw_parse_pll_data(pTAS2563, pFirmware, pData);
+
+       pData += nPosition;
+       nSize -= nPosition;
+       nPosition = 0;
+*/
+
+       nPosition = fw_parse_program_data(pTAS2563, pFirmware, pData);
+       dev_info(pTAS2563->dev, "program size: %d, line: %d\n", nPosition, __LINE__);
+
+       pData += nPosition;
+       nSize -= nPosition;
+       nPosition = 0;
+
+       nPosition = fw_parse_configuration_data(pTAS2563, pFirmware, pData);
+       dev_info(pTAS2563->dev, "config size: %d, line: %d\n", nPosition, __LINE__);
+
+       pData += nPosition;
+       nSize -= nPosition;
+       nPosition = 0;
+
+       if (nSize > 64)
+               nPosition = fw_parse_calibration_data(pTAS2563, pFirmware, pData);
+       dev_info(pTAS2563->dev, "calib size: %d, line: %d\n", nPosition, __LINE__);
+       return 0;
+}
+
+
+static int tas2563_codec_suspend(struct snd_soc_codec *codec)
+{
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       mutex_lock(&pTAS2563->codec_lock);
+
+       dev_info(pTAS2563->dev, "%s\n", __func__);
+       pTAS2563->runtime_suspend(pTAS2563);
+
+       mutex_unlock(&pTAS2563->codec_lock);
+       return ret;
+}
+
+static int tas2563_codec_resume(struct snd_soc_codec *codec)
+{
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       mutex_lock(&pTAS2563->codec_lock);
+
+       dev_info(pTAS2563->dev, "%s\n", __func__);
+       pTAS2563->runtime_resume(pTAS2563);
+
+       mutex_unlock(&pTAS2563->codec_lock);
+       return ret;
+}
+
+static const struct snd_kcontrol_new tas2563_asi_controls[] = {
+       SOC_DAPM_SINGLE("Left", TAS2563_TDMConfigurationReg2,
+               4, 1, 0),
+       SOC_DAPM_SINGLE("Right", TAS2563_TDMConfigurationReg2,
+               4, 2, 0),
+       SOC_DAPM_SINGLE("LeftRightDiv2", TAS2563_TDMConfigurationReg2,
+               4, 3, 0),
+};
+
+static int tas2563_set_power_state(struct tas2563_priv *pTAS2563, int state)
+{
+       int nResult = 0;
+       /*unsigned int nValue;*/
+       const char *pFWName;
+       struct TProgram *pProgram;
+
+       dev_info(pTAS2563->dev, "set power state: %d\n", state);
+
+       if ((pTAS2563->mpFirmware->mnPrograms == 0)
+               || (pTAS2563->mpFirmware->mnConfigurations == 0)) {
+               dev_err(pTAS2563->dev, "%s, firmware not loaded\n", __func__);
+               pFWName = TAS2563_FW_NAME;
+               nResult = request_firmware_nowait(THIS_MODULE, 1, pFWName,
+                       pTAS2563->dev, GFP_KERNEL, pTAS2563, tas2563_fw_ready);
+
+               if(nResult < 0) {
+                       dev_err(pTAS2563->dev, "%s, firmware is loaded, %d\n", __func__, nResult);
+                       goto end;
+               }
+       }
+       /* check safe guard*/
+       /* TBD, add back when FW ready
+       nResult = pTAS2563->read(pTAS2563, TAS2563_SAFE_GUARD_REG, &nValue);
+       if (nResult < 0)
+               goto end;
+       if ((nValue&0xff) != TAS2563_SAFE_GUARD_PATTERN) {
+               dev_err(pTAS2563->dev, "ERROR safe guard failure!\n");
+               nResult = -EPIPE;
+               goto end;
+       }
+       */
+       
+       pProgram = &(pTAS2563->mpFirmware->mpPrograms[pTAS2563->mnCurrentProgram]);
+       dev_info(pTAS2563->dev, "%s, state: %d, mbPowerup %d\n", __func__, state, pTAS2563->mbPowerUp);
+       if (state != TAS2563_POWER_SHUTDOWN) {
+               if (!pTAS2563->mbPowerUp) {
+                       if (!pTAS2563->mbCalibrationLoaded) {
+                               nResult = tas2563_set_calibration(pTAS2563, 0xFF);
+                               if((nResult > 0) || (nResult == 0))
+                                       pTAS2563->mbCalibrationLoaded = true;
+                       }
+
+                       if (pTAS2563->mbLoadConfigurationPrePowerUp) {
+                               dev_info(pTAS2563->dev, "load coefficient before power\n");
+                               pTAS2563->mbLoadConfigurationPrePowerUp = false;
+                               nResult = tas2563_load_coefficient(pTAS2563,
+                                       pTAS2563->mnCurrentConfiguration, pTAS2563->mnNewConfiguration, false);
+                               if (nResult < 0)
+                                       goto end;
+                       }
+               }
+       }
+
+       switch (state) {
+       case TAS2563_POWER_ACTIVE:
+               nResult = pTAS2563->update_bits(pTAS2563, TAS2563_PowerControl,
+                       TAS2563_PowerControl_OperationalMode10_Mask |
+                       TAS2563_PowerControl_ISNSPower_Mask |
+                       TAS2563_PowerControl_VSNSPower_Mask,
+                       TAS2563_PowerControl_OperationalMode10_Active |
+                       TAS2563_PowerControl_VSNSPower_Active |
+                       TAS2563_PowerControl_ISNSPower_Active);
+               if (nResult < 0)
+                       return nResult;
+               pTAS2563->mbPowerUp = true;
+               dev_info(pTAS2563->dev, "set ICN to -80dB\n");
+               nResult = pTAS2563->bulk_write(pTAS2563, TAS2563_ICN_REG, pICN, 4);
+               break;
+
+       case TAS2563_POWER_MUTE:
+               nResult = pTAS2563->update_bits(pTAS2563, TAS2563_PowerControl,
+                       TAS2563_PowerControl_OperationalMode10_Mask |
+                       TAS2563_PowerControl_ISNSPower_Mask |
+                       TAS2563_PowerControl_VSNSPower_Mask,
+                       TAS2563_PowerControl_OperationalMode10_Mute |
+                       TAS2563_PowerControl_VSNSPower_Active |
+                       TAS2563_PowerControl_ISNSPower_Active);
+                       pTAS2563->mbPowerUp = true;
+               break;
+
+       case TAS2563_POWER_SHUTDOWN:
+               nResult = pTAS2563->update_bits(pTAS2563, TAS2563_PowerControl,
+                       TAS2563_PowerControl_OperationalMode10_Mask,
+                       TAS2563_PowerControl_OperationalMode10_Shutdown);
+                       pTAS2563->mbPowerUp = false;
+               break;
+
+       default:
+               dev_err(pTAS2563->dev, "wrong power state setting %d\n", state);
+
+       }
+
+end:
+       pTAS2563->mnPowerState = state;
+       return nResult;
+}
+
+static int tas2563_dac_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *kcontrol, int event)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+#else
+       struct snd_soc_codec *codec = w->codec;
+#endif
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               tas2563_set_power_state(pTAS2563, TAS2563_POWER_ACTIVE);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               tas2563_set_power_state(pTAS2563, TAS2563_POWER_SHUTDOWN);
+               break;
+
+       }
+       return 0;
+
+}
+
+static const struct snd_soc_dapm_widget tas2563_dapm_widgets[] = {
+       SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("Voltage Sense", "ASI1 Capture",  1, TAS2563_PowerControl, 2, 1),
+       SND_SOC_DAPM_AIF_OUT("Current Sense", "ASI1 Capture",  0, TAS2563_PowerControl, 3, 1),
+       SND_SOC_DAPM_MIXER("ASI1 Sel",
+               TAS2563_TDMConfigurationReg2, 4, 0,
+               &tas2563_asi_controls[0],
+               ARRAY_SIZE(tas2563_asi_controls)),
+       SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2563_dac_event,
+       SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_OUTPUT("OUT"),
+       SND_SOC_DAPM_SIGGEN("VMON"),
+       SND_SOC_DAPM_SIGGEN("IMON")
+};
+
+static const struct snd_soc_dapm_route tas2563_audio_map[] = {
+       {"ASI1 Sel", "Left", "ASI1"},
+       {"ASI1 Sel", "Right", "ASI1"},
+       {"ASI1 Sel", "LeftRightDiv2", "ASI1"},
+       {"DAC", NULL, "ASI1 Sel"},
+       {"OUT", NULL, "DAC"},
+       /*{"VMON", NULL, "Voltage Sense"},
+       {"IMON", NULL, "Current Sense"},*/
+       {"Voltage Sense", NULL, "VMON"},
+       {"Current Sense", NULL, "IMON"},
+};
+
+
+static int tas2563_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+
+        dev_info(pTAS2563->dev, "%s\n", __func__);
+       mutex_lock(&pTAS2563->codec_lock);
+       if (mute) {
+                dev_info(pTAS2563->dev, "mute: %s\n", __func__);
+               tas2563_set_power_state(pTAS2563, TAS2563_POWER_SHUTDOWN);
+       } else {
+                dev_info(pTAS2563->dev, "unmute: %s\n", __func__);
+               tas2563_set_power_state(pTAS2563, TAS2563_POWER_ACTIVE);
+       }
+       mutex_unlock(&pTAS2563->codec_lock);
+       return 0;
+}
+
+#if 1
+static int tas2563_slot_config(struct snd_soc_codec *codec, struct tas2563_priv *pTAS2563, int blr_clk_ratio)
+{
+       int ret = 0;
+       pTAS2563->update_bits(pTAS2563,
+                       TAS2563_TDMConfigurationReg5, 0xff, 0x42);
+
+       pTAS2563->update_bits(pTAS2563,
+                       TAS2563_TDMConfigurationReg6, 0xff, 0x40);
+
+       return ret;
+}
+#endif
+
+static int tas2563_set_slot(struct tas2563_priv *pTAS2563, int slot_width)
+{
+       int ret = 0;
+       dev_info(pTAS2563->dev, "%s, %d, ret = %d", __func__, __LINE__, ret);
+
+       switch (slot_width) {
+       case 16:
+       ret = pTAS2563->update_bits(pTAS2563, 
+               TAS2563_TDMConfigurationReg2,
+               TAS2563_TDMConfigurationReg2_RXSLEN10_Mask,
+               TAS2563_TDMConfigurationReg2_RXSLEN10_16Bits);
+       break;
+
+       case 24:
+       ret = pTAS2563->update_bits(pTAS2563, 
+               TAS2563_TDMConfigurationReg2,
+               TAS2563_TDMConfigurationReg2_RXSLEN10_Mask,
+               TAS2563_TDMConfigurationReg2_RXSLEN10_24Bits);
+       break;
+
+       case 32:
+       ret = pTAS2563->update_bits(pTAS2563, 
+               TAS2563_TDMConfigurationReg2,
+               TAS2563_TDMConfigurationReg2_RXSLEN10_Mask,
+               TAS2563_TDMConfigurationReg2_RXSLEN10_32Bits);
+       break;
+
+       case 0:
+       /* Do not change slot width */
+       break;
+
+       default:
+               dev_info(pTAS2563->dev, "slot width not supported");
+               ret = -EINVAL;
+       }
+
+       if (ret >= 0)
+               pTAS2563->mnSlot_width = slot_width;
+
+       return ret;
+}
+
+static int tas2563_set_bitwidth(struct tas2563_priv *pTAS2563, int bitwidth)
+{
+       int slot_width_tmp = 16;
+       dev_info(pTAS2563->dev, "%s %d\n", __func__, __LINE__);
+
+       switch (bitwidth) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+                       pTAS2563->update_bits(pTAS2563, 
+                       TAS2563_TDMConfigurationReg2,
+                       TAS2563_TDMConfigurationReg2_RXWLEN32_Mask,
+                       TAS2563_TDMConfigurationReg2_RXWLEN32_16Bits);
+                       pTAS2563->mnCh_size = 16;
+                       if (pTAS2563->mnSlot_width == 0)
+                               slot_width_tmp = 16;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+                       pTAS2563->update_bits(pTAS2563, 
+                       TAS2563_TDMConfigurationReg2,
+                       TAS2563_TDMConfigurationReg2_RXWLEN32_Mask,
+                       TAS2563_TDMConfigurationReg2_RXWLEN32_24Bits);
+                       pTAS2563->mnCh_size = 24;
+                       if (pTAS2563->mnSlot_width == 0)
+                               slot_width_tmp = 32;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+                       pTAS2563->update_bits(pTAS2563,
+                       TAS2563_TDMConfigurationReg2,
+                       TAS2563_TDMConfigurationReg2_RXWLEN32_Mask,
+                       TAS2563_TDMConfigurationReg2_RXWLEN32_32Bits);
+                       pTAS2563->mnCh_size = 32;
+                       if (pTAS2563->mnSlot_width == 0)
+                               slot_width_tmp = 32;
+               break;
+
+       default:
+               dev_info(pTAS2563->dev, "Not supported params format\n");
+       }
+
+       /* If machine driver did not call set slot width */
+       if (pTAS2563->mnSlot_width == 0)
+               tas2563_set_slot(pTAS2563, slot_width_tmp);
+
+       dev_info(pTAS2563->dev, "mnCh_size: %d\n", pTAS2563->mnCh_size);
+       pTAS2563->mnPCMFormat = bitwidth;
+
+       return 0;
+}
+
+static int tas2563_set_samplerate(struct tas2563_priv *pTAS2563, int samplerate)
+{
+       switch (samplerate) {
+       case 48000:
+                       pTAS2563->update_bits(pTAS2563,
+                               TAS2563_TDMConfigurationReg0,
+                               TAS2563_TDMConfigurationReg0_SAMPRATERAMP_Mask,
+                               TAS2563_TDMConfigurationReg0_SAMPRATERAMP_48KHz);
+                       pTAS2563->update_bits(pTAS2563,
+                               TAS2563_TDMConfigurationReg0,
+                               TAS2563_TDMConfigurationReg0_SAMPRATE31_Mask,
+                               TAS2563_TDMConfigurationReg0_SAMPRATE31_44_1_48kHz);
+                       break;
+       case 44100:
+                       pTAS2563->update_bits(pTAS2563,
+                               TAS2563_TDMConfigurationReg0,
+                               TAS2563_TDMConfigurationReg0_SAMPRATERAMP_Mask,
+                               TAS2563_TDMConfigurationReg0_SAMPRATERAMP_44_1KHz);
+                       pTAS2563->update_bits(pTAS2563,
+                               TAS2563_TDMConfigurationReg0,
+                               TAS2563_TDMConfigurationReg0_SAMPRATE31_Mask,
+                               TAS2563_TDMConfigurationReg0_SAMPRATE31_44_1_48kHz);
+                       break;
+       case 96000:
+                       pTAS2563->update_bits(pTAS2563,
+                               TAS2563_TDMConfigurationReg0,
+                               TAS2563_TDMConfigurationReg0_SAMPRATERAMP_Mask,
+                               TAS2563_TDMConfigurationReg0_SAMPRATERAMP_48KHz);
+                       pTAS2563->update_bits(pTAS2563,
+                               TAS2563_TDMConfigurationReg0,
+                               TAS2563_TDMConfigurationReg0_SAMPRATE31_Mask,
+                               TAS2563_TDMConfigurationReg0_SAMPRATE31_88_2_96kHz);
+                       break;
+       case 88200:
+                       pTAS2563->update_bits(pTAS2563,
+                               TAS2563_TDMConfigurationReg0,
+                               TAS2563_TDMConfigurationReg0_SAMPRATERAMP_Mask,
+                               TAS2563_TDMConfigurationReg0_SAMPRATERAMP_44_1KHz);
+                       pTAS2563->update_bits(pTAS2563,
+                               TAS2563_TDMConfigurationReg0,
+                               TAS2563_TDMConfigurationReg0_SAMPRATE31_Mask,
+                               TAS2563_TDMConfigurationReg0_SAMPRATE31_88_2_96kHz);
+                       break;
+       case 19200:
+                       pTAS2563->update_bits(pTAS2563,
+                               TAS2563_TDMConfigurationReg0,
+                               TAS2563_TDMConfigurationReg0_SAMPRATERAMP_Mask,
+                               TAS2563_TDMConfigurationReg0_SAMPRATERAMP_48KHz);
+                       pTAS2563->update_bits(pTAS2563,
+                               TAS2563_TDMConfigurationReg0,
+                               TAS2563_TDMConfigurationReg0_SAMPRATE31_Mask,
+                               TAS2563_TDMConfigurationReg0_SAMPRATE31_176_4_192kHz);
+                       break;
+       case 17640:
+                       pTAS2563->update_bits(pTAS2563,
+                               TAS2563_TDMConfigurationReg0,
+                               TAS2563_TDMConfigurationReg0_SAMPRATERAMP_Mask,
+                               TAS2563_TDMConfigurationReg0_SAMPRATERAMP_44_1KHz);
+                       pTAS2563->update_bits(pTAS2563,
+                               TAS2563_TDMConfigurationReg0,
+                               TAS2563_TDMConfigurationReg0_SAMPRATE31_Mask,
+                               TAS2563_TDMConfigurationReg0_SAMPRATE31_176_4_192kHz);
+                       break;
+       default:
+                       dev_info(pTAS2563->dev, "%s, unsupported sample rate, %d\n", __func__, samplerate);
+
+       }
+
+       pTAS2563->mnSamplingRate = samplerate;
+       return 0;
+}
+
+int tas2563_load_default(struct tas2563_priv *pTAS2563)
+{
+       int ret = 0;
+       
+       dev_info(pTAS2563->dev, "%s, %d, ret = %d", __func__, __LINE__, ret);
+
+       ret = tas2563_set_slot(pTAS2563, pTAS2563->mnSlot_width);
+       if (ret < 0)
+               goto end;
+       dev_info(pTAS2563->dev, "%s, %d, ret = %d", __func__, __LINE__, ret);
+       
+        //if set format was not called by asoc, then set it default
+       if(pTAS2563->mnASIFormat == 0)
+                pTAS2563->mnASIFormat = SND_SOC_DAIFMT_CBS_CFS 
+                               | SND_SOC_DAIFMT_IB_NF 
+                               | SND_SOC_DAIFMT_I2S;
+       ret = tas2563_set_fmt(pTAS2563, pTAS2563->mnASIFormat);
+
+       if (ret < 0)
+               goto end;
+       dev_info(pTAS2563->dev, "%s, %d, ret = %d", __func__, __LINE__, ret);
+
+       ret = tas2563_set_bitwidth(pTAS2563, pTAS2563->mnPCMFormat);
+       if (ret < 0)
+               goto end;
+       dev_info(pTAS2563->dev, "%s, %d, ret = %d", __func__, __LINE__, ret);
+
+       ret = tas2563_set_samplerate(pTAS2563, pTAS2563->mnSamplingRate);
+       if (ret < 0)
+               goto end;
+
+end:
+/* power up failed, restart later */
+       dev_info(pTAS2563->dev, "%s, %d, ret = %d", __func__, __LINE__, ret);
+       if (ret < 0)
+               schedule_delayed_work(&pTAS2563->irq_work,
+                               msecs_to_jiffies(1000));
+       return ret;
+}
+
+#if 0
+static void failsafe(struct tas2563_priv *pTAS2563)
+{
+       dev_err(pTAS2563->dev, "%s\n", __func__);
+       pTAS2563->mnErrCode |= ERROR_FAILSAFE;
+       if (hrtimer_active(&pTAS2563->mtimerwork))
+               hrtimer_cancel(&pTAS2563->mtimerwork);
+
+       if(pTAS2563->mnRestart < RESTART_MAX)
+       {
+               pTAS2563->mnRestart ++;
+               msleep(100);
+               dev_err(pTAS2563->dev, "I2C COMM error, restart SmartAmp.\n");
+               schedule_delayed_work(&pTAS2563->irq_work, msecs_to_jiffies(100));
+               return;
+       }
+       pTAS2563->enableIRQ(pTAS2563, false);
+       tas2563_set_power_state(pTAS2563, TAS2563_POWER_SHUTDOWN);
+
+       pTAS2563->mbPowerUp = false;
+       pTAS2563->hw_reset(pTAS2563);
+       pTAS2563->write(pTAS2563, TAS2563_SoftwareReset, TAS2563_SoftwareReset_SoftwareReset_Reset);
+       udelay(1000);
+       if (pTAS2563->mpFirmware != NULL)
+               tas2563_clear_firmware(pTAS2563->mpFirmware);
+}
+#endif
+
+/*
+* tas2563_load_coefficient
+*/
+static int tas2563_load_coefficient(struct tas2563_priv *pTAS2563,
+       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 (!pTAS2563->mpFirmware->mnConfigurations) {
+               dev_err(pTAS2563->dev, "%s, firmware not loaded\n", __func__);
+               goto end;
+       }
+
+       if (nNewConfig >= pTAS2563->mpFirmware->mnConfigurations) {
+               dev_err(pTAS2563->dev, "%s, invalid configuration New=%d, total=%d\n",
+                       __func__, nNewConfig, pTAS2563->mpFirmware->mnConfigurations);
+               goto end;
+       }
+
+       if (nPrevConfig < 0)
+               pPrevConfiguration = NULL;
+       else if (nPrevConfig == nNewConfig) {
+               dev_info(pTAS2563->dev, "%s, config [%d] already loaded\n",
+                       __func__, nNewConfig);
+               goto end;
+       } else
+               pPrevConfiguration = &(pTAS2563->mpFirmware->mpConfigurations[nPrevConfig]);
+
+       pNewConfiguration = &(pTAS2563->mpFirmware->mpConfigurations[nNewConfig]);
+       pTAS2563->mnCurrentConfiguration = nNewConfig;
+#if 0
+       if (pPrevConfiguration) {
+               if (pPrevConfiguration->mnPLL == pNewConfiguration->mnPLL) {
+                       dev_info(pTAS2563->dev, "%s, PLL same\n", __func__);
+                       goto prog_coefficient;
+               }
+       }
+
+       pProgram = &(pTAS2563->mpFirmware->mpPrograms[pTAS2563->mnCurrentProgram]);
+       if (bPowerOn) {
+               dev_info(pTAS2563->dev, "%s, power down to load new PLL\n", __func__);
+               if (hrtimer_active(&pTAS2563->mtimerwork))
+                       hrtimer_cancel(&pTAS2563->mtimerwork);
+
+               if (pProgram->mnAppMode == TAS2563_APP_TUNINGMODE)
+                       pTAS2563->enableIRQ(pTAS2563, false);
+
+               nResult = tas2563_set_power_state(pTAS2563, TAS2563_POWER_SHUTDOWN);
+               if (nResult < 0)
+                       goto end;
+               bRestorePower = true;
+       }
+
+       /* load PLL */
+       pPLL = &(pTAS2563->mpFirmware->mpPLLs[pNewConfiguration->mnPLL]);
+       dev_info(pTAS2563->dev, "load PLL: %s block for Configuration %s\n",
+               pPLL->mpName, pNewConfiguration->mpName);
+       nResult = tas2563_load_block(pTAS2563, &(pPLL->mBlock));
+       if (nResult < 0)
+               goto end;
+#endif
+       pTAS2563->mnCurrentSampleRate = pNewConfiguration->mnSamplingRate;
+
+       dev_info(pTAS2563->dev, "load configuration %s conefficient pre block\n",
+               pNewConfiguration->mpName);
+       nResult = tas2563_load_data(pTAS2563, &(pNewConfiguration->mData), TAS2563_BLOCK_CFG_PRE_DEV_A);
+       if (nResult < 0)
+               goto end;
+
+//prog_coefficient:
+       dev_info(pTAS2563->dev, "load new configuration: %s, coeff block data\n",
+               pNewConfiguration->mpName);
+       nResult = tas2563_load_data(pTAS2563, &(pNewConfiguration->mData),
+               TAS2563_BLOCK_CFG_COEFF_DEV_A);
+       if (nResult < 0)
+               goto end;
+
+       if (pTAS2563->mpCalFirmware->mnCalibrations) {
+               nResult = tas2563_set_calibration(pTAS2563, pTAS2563->mnCurrentCalibration);
+               if (nResult < 0)
+                       goto end;
+       }
+
+       if (bRestorePower) {
+               pTAS2563->clearIRQ(pTAS2563);
+               dev_info(pTAS2563->dev, "device powered up, load startup\n");
+               nResult = tas2563_set_power_state(pTAS2563, TAS2563_POWER_MUTE);
+               if (nResult < 0)
+                       goto end;
+
+               dev_info(pTAS2563->dev,
+                       "device powered up, load unmute\n");
+               nResult = tas2563_set_power_state(pTAS2563, TAS2563_POWER_ACTIVE);
+               if (nResult < 0)
+                       goto end;
+               if (pProgram->mnAppMode == TAS2563_APP_TUNINGMODE) {
+                       pTAS2563->enableIRQ(pTAS2563, true);
+                       if (!hrtimer_active(&pTAS2563->mtimerwork)) {
+                               pTAS2563->mnDieTvReadCounter = 0;
+                               hrtimer_start(&pTAS2563->mtimerwork,
+                                       ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+                       }
+               }
+       }
+end:
+
+       pTAS2563->mnNewConfiguration = pTAS2563->mnCurrentConfiguration;
+       return nResult;
+}
+
+static bool tas2563_get_coefficient_in_data(struct tas2563_priv *pTAS2563,
+       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 = tas2563_get_coefficient_in_block(pTAS2563,
+                                               pBlock, nReg, pnValue);
+                       if (bFound)
+                               break;
+               }
+       }
+
+       return bFound;
+}
+
+static bool tas2563_find_Tmax_in_configuration(struct tas2563_priv *pTAS2563,
+       struct TConfiguration *pConfiguration, int *pnTMax)
+{
+       struct TData *pData;
+       bool bFound = false;
+       int nBlockType, nReg, nCoefficient;
+
+       nReg = TAS2563_CALI_T_REG;
+
+       nBlockType = TAS2563_BLOCK_CFG_COEFF_DEV_A;
+
+       pData = &(pConfiguration->mData);
+       bFound = tas2563_get_coefficient_in_data(pTAS2563, pData, nBlockType, nReg, &nCoefficient);
+       if (bFound)
+               *pnTMax = nCoefficient;
+
+       return bFound;
+}
+
+void tas2563_fw_ready(const struct firmware *pFW, void *pContext)
+{
+       struct tas2563_priv *pTAS2563 = (struct tas2563_priv *) pContext;
+       int nResult;
+       unsigned int nProgram = 0;
+       unsigned int nSampleRate = 0;
+
+#ifdef CONFIG_TAS2563_CODEC
+       mutex_lock(&pTAS2563->codec_lock);
+#endif
+
+#ifdef CONFIG_TAS2563_MISC
+       mutex_lock(&pTAS2563->file_lock);
+#endif
+
+       dev_info(pTAS2563->dev, "%s:\n", __func__);
+
+       if (unlikely(!pFW) || unlikely(!pFW->data)) {
+               dev_err(pTAS2563->dev, "%s firmware is not loaded.\n",
+                       TAS2563_FW_NAME);
+               goto end;
+       }
+
+       if (pTAS2563->mpFirmware->mpConfigurations) {
+               nProgram = pTAS2563->mnCurrentProgram;
+               nSampleRate = pTAS2563->mnCurrentSampleRate;
+               dev_info(pTAS2563->dev, "clear current firmware\n");
+               tas2563_clear_firmware(pTAS2563->mpFirmware);
+       }
+
+       nResult = fw_parse(pTAS2563, pTAS2563->mpFirmware, (unsigned char *)(pFW->data), pFW->size);
+       release_firmware(pFW);
+       if (nResult < 0) {
+               dev_err(pTAS2563->dev, "firmware is corrupt\n");
+               goto end;
+       }
+
+       if (!pTAS2563->mpFirmware->mnPrograms) {
+               dev_err(pTAS2563->dev, "firmware contains no programs\n");
+               nResult = -EINVAL;
+               goto end;
+       }
+
+       if (!pTAS2563->mpFirmware->mnConfigurations) {
+               dev_err(pTAS2563->dev, "firmware contains no configurations\n");
+               nResult = -EINVAL;
+               goto end;
+       }
+
+       if (nProgram >= pTAS2563->mpFirmware->mnPrograms) {
+               dev_info(pTAS2563->dev,
+                       "no previous program, set to default\n");
+               nProgram = 0;
+       }
+
+       pTAS2563->mnCurrentSampleRate = nSampleRate;
+       nResult = tas2563_set_program(pTAS2563, nProgram, -1);
+
+end:
+
+#ifdef CONFIG_TAS2563_CODEC
+       mutex_unlock(&pTAS2563->codec_lock);
+#endif
+
+#ifdef CONFIG_TAS2563_MISC
+       mutex_unlock(&pTAS2563->file_lock);
+#endif
+}
+
+static bool tas2563_get_coefficient_in_block(struct tas2563_priv *pTAS2563,
+       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 != TAS2563_BOOK_ID(nReg))
+                               || (nPage != TAS2563_PAGE_ID(nReg)))
+                               continue;
+                       if (nOffset > TAS2563_PAGE_REG(nReg))
+                               continue;
+                       if ((len + nOffset) >= (TAS2563_PAGE_REG(nReg) + 4)) {
+                               n += (TAS2563_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(pTAS2563->dev, "%s, format error %d\n", __func__, nOffset);
+                       break;
+               }
+       }
+
+       if (bFound) {
+               *pnValue = nCoefficient;
+               dev_info(pTAS2563->dev, "%s, B[0x%x]P[0x%x]R[0x%x]=0x%x\n", __func__,
+                       TAS2563_BOOK_ID(nReg), TAS2563_PAGE_ID(nReg), TAS2563_PAGE_REG(nReg),
+                       nCoefficient);
+       }
+
+       return bFound;
+}
+
+
+int tas2563_set_program(struct tas2563_priv *pTAS2563,
+       unsigned int nProgram, int nConfig)
+{
+       struct TProgram *pProgram;
+       unsigned int nConfiguration = 0;
+       unsigned int nSampleRate = 0;
+       bool bFound = false;
+       int nResult = 0;
+
+       if ((!pTAS2563->mpFirmware->mpPrograms) ||
+               (!pTAS2563->mpFirmware->mpConfigurations)) {
+               dev_err(pTAS2563->dev, "%s, Firmware not loaded\n", __func__);
+               nResult = 0;
+               goto end;
+       }
+
+       if (nProgram >= pTAS2563->mpFirmware->mnPrograms) {
+               dev_err(pTAS2563->dev, "TAS2563: Program %d doesn't exist\n",
+                       nProgram);
+               nResult = 0;
+               goto end;
+       }
+
+       if (nConfig < 0) {
+               nConfiguration = 0;
+               nSampleRate = pTAS2563->mnCurrentSampleRate;
+               while (!bFound && (nConfiguration < pTAS2563->mpFirmware->mnConfigurations)) {
+                       if (pTAS2563->mpFirmware->mpConfigurations[nConfiguration].mnProgram == nProgram) {
+                               if (nSampleRate == 0) {
+                                       bFound = true;
+                                       dev_info(pTAS2563->dev, "find default configuration %d\n", nConfiguration);
+                               } else if (nSampleRate == pTAS2563->mpFirmware->mpConfigurations[nConfiguration].mnSamplingRate) {
+                                       bFound = true;
+                                       dev_info(pTAS2563->dev, "find matching configuration %d\n", nConfiguration);
+                               } else {
+                                       nConfiguration++;
+                               }
+                       } else {
+                               nConfiguration++;
+                       }
+               }
+               if (!bFound) {
+                       dev_err(pTAS2563->dev,
+                               "Program %d, no valid configuration found for sample rate %d, ignore\n",
+                               nProgram, nSampleRate);
+                       nResult = 0;
+                       goto end;
+               }
+       } else {
+               if (pTAS2563->mpFirmware->mpConfigurations[nConfig].mnProgram != nProgram) {
+                       dev_err(pTAS2563->dev, "%s, configuration program doesn't match\n", __func__);
+                       nResult = 0;
+                       goto end;
+               }
+               nConfiguration = nConfig;
+       }
+
+       pProgram = &(pTAS2563->mpFirmware->mpPrograms[nProgram]);
+       if (pTAS2563->mbPowerUp) {
+               dev_info(pTAS2563->dev,
+                       "device powered up, power down to load program %d (%s)\n",
+                       nProgram, pProgram->mpName);
+               if (hrtimer_active(&pTAS2563->mtimerwork))
+                       hrtimer_cancel(&pTAS2563->mtimerwork);
+
+               if (pProgram->mnAppMode == TAS2563_APP_TUNINGMODE)
+                       pTAS2563->enableIRQ(pTAS2563, false);
+
+               nResult = tas2563_set_power_state(pTAS2563, TAS2563_POWER_SHUTDOWN);
+               if (nResult < 0)
+                       goto end;
+       }
+
+       pTAS2563->hw_reset(pTAS2563);
+       nResult = pTAS2563->write(pTAS2563, TAS2563_SoftwareReset, 0x01);
+       if (nResult < 0)
+               goto end;
+       msleep(1);
+       nResult = tas2563_load_default(pTAS2563);
+       if (nResult < 0)
+               goto end;
+
+       dev_info(pTAS2563->dev, "load program %d (%s)\n", nProgram, pProgram->mpName);
+       nResult = tas2563_load_data(pTAS2563, &(pProgram->mData), TAS2563_BLOCK_PGM_DEV_A);
+       if (nResult < 0)
+               goto end;
+       pTAS2563->mnCurrentProgram = nProgram;
+
+       nResult = tas2563_load_coefficient(pTAS2563, -1, nConfiguration, false);
+       if (nResult < 0)
+               goto end;
+
+       // Enable IV data
+       nResult = pTAS2563->update_bits(pTAS2563, TAS2563_PowerControl,
+                                       TAS2563_PowerControl_OperationalMode10_Mask |
+                               TAS2563_PowerControl_ISNSPower_Mask |
+                               TAS2563_PowerControl_VSNSPower_Mask,
+                               TAS2563_PowerControl_OperationalMode10_Active |
+                               TAS2563_PowerControl_VSNSPower_Active |
+                               TAS2563_PowerControl_ISNSPower_Active);
+       if (nResult < 0)
+               dev_info(pTAS2563->dev, "Enable IV Data Failed: %s\n", __func__);
+
+       if (pTAS2563->mbPowerUp) {
+//             pTAS2563->clearIRQ(pTAS2563);
+//             dev_info(pTAS2563->dev, "device powered up, load startup\n");
+               nResult = tas2563_set_power_state(pTAS2563, TAS2563_POWER_MUTE);
+
+               if (nResult < 0)
+                       goto end;
+               if (pProgram->mnAppMode == TAS2563_APP_TUNINGMODE) {
+//                     nResult = tas2563_checkPLL(pTAS2563);
+                       if (nResult < 0) {
+                               tas2563_set_power_state(pTAS2563, TAS2563_POWER_SHUTDOWN);
+                               pTAS2563->mbPowerUp = false;
+                               goto end;
+                       }
+               }
+               dev_info(pTAS2563->dev, "device powered up, load unmute\n");
+               tas2563_set_power_state(pTAS2563, TAS2563_POWER_ACTIVE);
+               if (nResult < 0)
+                       goto end;
+
+               if (pProgram->mnAppMode == TAS2563_APP_TUNINGMODE) {
+                       pTAS2563->enableIRQ(pTAS2563, true);
+                       if (!hrtimer_active(&pTAS2563->mtimerwork)) {
+                               pTAS2563->mnDieTvReadCounter = 0;
+                               hrtimer_start(&pTAS2563->mtimerwork,
+                                       ns_to_ktime((u64)LOW_TEMPERATURE_CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL);
+                       }
+               }
+       }
+
+end:
+
+/*     if (nResult < 0) {
+               if (pTAS2563->mnErrCode & (ERROR_DEVA_I2C_COMM | ERROR_PRAM_CRCCHK | ERROR_YRAM_CRCCHK))
+                       failsafe(pTAS2563);
+       }*/
+       return nResult;
+}
+
+static int tas2563_set_calibration(struct tas2563_priv *pTAS2563, int nCalibration)
+{
+       struct TCalibration *pCalibration = NULL;
+       struct TConfiguration *pConfiguration;
+       struct TProgram *pProgram;
+       int nTmax = 0;
+       bool bFound = false;
+       int nResult = 0;
+
+       if ((!pTAS2563->mpFirmware->mpPrograms)
+               || (!pTAS2563->mpFirmware->mpConfigurations)) {
+               dev_err(pTAS2563->dev, "%s, Firmware not loaded\n\r", __func__);
+               nResult = 0;
+               goto end;
+       }
+
+       if (nCalibration == 0x00FF) {
+               nResult = tas2563_load_calibration(pTAS2563, TAS2563_CAL_NAME);
+               if (nResult < 0) {
+                       dev_info(pTAS2563->dev, "load new calibration file %s fail %d\n",
+                               TAS2563_CAL_NAME, nResult);
+                       goto end;
+               }
+               nCalibration = 0;
+       }
+
+       if (nCalibration >= pTAS2563->mpCalFirmware->mnCalibrations) {
+               dev_err(pTAS2563->dev,
+                       "Calibration %d doesn't exist\n", nCalibration);
+               nResult = 0;
+               goto end;
+       }
+
+       pTAS2563->mnCurrentCalibration = nCalibration;
+       if (pTAS2563->mbLoadConfigurationPrePowerUp)
+               goto end;
+
+       pCalibration = &(pTAS2563->mpCalFirmware->mpCalibrations[nCalibration]);
+       pProgram = &(pTAS2563->mpFirmware->mpPrograms[pTAS2563->mnCurrentProgram]);
+       pConfiguration = &(pTAS2563->mpFirmware->mpConfigurations[pTAS2563->mnCurrentConfiguration]);
+       if (pProgram->mnAppMode == TAS2563_APP_TUNINGMODE) {
+               if (pTAS2563->mbBypassTMax) {
+                       bFound = tas2563_find_Tmax_in_configuration(pTAS2563, pConfiguration, &nTmax);
+                       if (bFound && (nTmax == TAS2563_COEFFICIENT_TMAX)) {
+                               dev_info(pTAS2563->dev, "%s, config[%s] bypass load calibration\n",
+                                       __func__, pConfiguration->mpName);
+                               goto end;
+                       }
+               }
+
+               dev_info(pTAS2563->dev, "%s, load calibration\n", __func__);
+               nResult = tas2563_load_data(pTAS2563, &(pCalibration->mData), TAS2563_BLOCK_CFG_COEFF_DEV_A);
+               if (nResult < 0)
+                       goto end;
+       }
+
+end:
+       if (nResult < 0) {
+               tas2563_clear_firmware(pTAS2563->mpCalFirmware);
+               nResult = tas2563_set_program(pTAS2563, pTAS2563->mnCurrentProgram, pTAS2563->mnCurrentConfiguration);
+       }
+
+       return nResult;
+}
+
+bool tas2563_get_Cali_prm_r0(struct tas2563_priv *pTAS2563, int *prm_r0)
+{
+       struct TCalibration *pCalibration;
+       struct TData *pData;
+       int nReg;
+       int nCali_Re;
+       bool bFound = false;
+       int nBlockType;
+
+       if (!pTAS2563->mpCalFirmware->mnCalibrations) {
+               dev_err(pTAS2563->dev, "%s, no calibration data\n", __func__);
+               goto end;
+       }
+
+       nReg = TAS2563_CALI_R0_REG;
+       nBlockType = TAS2563_BLOCK_CFG_COEFF_DEV_A;
+
+       pCalibration = &(pTAS2563->mpCalFirmware->mpCalibrations[pTAS2563->mnCurrentCalibration]);
+       pData = &(pCalibration->mData);
+
+       bFound = tas2563_get_coefficient_in_data(pTAS2563, pData, nBlockType, nReg, &nCali_Re);
+
+end:
+
+       if (bFound)
+               *prm_r0 = nCali_Re;
+
+       return bFound;
+}
+
+int tas2563_set_config(struct tas2563_priv *pTAS2563, int config)
+{
+       struct TConfiguration *pConfiguration;
+       struct TProgram *pProgram;
+       unsigned int nProgram = pTAS2563->mnCurrentProgram;
+       unsigned int nConfiguration = config;
+       int nResult = 0;
+
+       if ((!pTAS2563->mpFirmware->mpPrograms) ||
+               (!pTAS2563->mpFirmware->mpConfigurations)) {
+               dev_err(pTAS2563->dev, "%s, Firmware not loaded\n", __func__);
+               nResult = -EINVAL;
+               goto end;
+       }
+
+       if (nConfiguration >= pTAS2563->mpFirmware->mnConfigurations) {
+               dev_err(pTAS2563->dev, "Configuration %d doesn't exist\n",
+                       nConfiguration);
+               nResult = -EINVAL;
+               goto end;
+       }
+
+       pConfiguration = &(pTAS2563->mpFirmware->mpConfigurations[nConfiguration]);
+       pProgram = &(pTAS2563->mpFirmware->mpPrograms[nProgram]);
+
+       if (nProgram != pConfiguration->mnProgram) {
+               dev_err(pTAS2563->dev,
+                       "Configuration %d, %s with Program %d isn't compatible with existing Program %d, %s\n",
+                       nConfiguration, pConfiguration->mpName, pConfiguration->mnProgram,
+                       nProgram, pProgram->mpName);
+               nResult = -EINVAL;
+               goto end;
+       }
+
+       nResult = tas2563_load_configuration(pTAS2563, nConfiguration, false);
+
+end:
+
+       return nResult;
+}
+
+static int tas2563_configuration_get(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+
+       mutex_lock(&pTAS2563->codec_lock);
+
+       pValue->value.integer.value[0] = pTAS2563->mnCurrentConfiguration;
+       dev_info(pTAS2563->dev, "tas2563_configuration_get = %d\n",
+               pTAS2563->mnCurrentConfiguration);
+
+       mutex_unlock(&pTAS2563->codec_lock);
+       return 0;
+}
+
+static int tas2563_configuration_put(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+       unsigned int nConfiguration = pValue->value.integer.value[0];
+       int ret = 0;
+
+       mutex_lock(&pTAS2563->codec_lock);
+
+       dev_info(pTAS2563->dev, "%s = %d\n", __func__, nConfiguration);
+       ret = tas2563_set_config(pTAS2563, nConfiguration);
+
+       mutex_unlock(&pTAS2563->codec_lock);
+       return ret;
+}
+
+
+static int tas2563_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params,
+               struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+       int blr_clk_ratio;
+
+       dev_info(pTAS2563->dev, "%s, format: %d\n", __func__,
+               params_format(params));
+
+       mutex_lock(&pTAS2563->codec_lock);
+
+       ret = tas2563_set_bitwidth(pTAS2563, params_format(params));
+       if(ret < 0)
+       {
+               dev_info(pTAS2563->dev, "set bitwidth failed, %d\n", ret);
+               goto ret;
+       }
+
+       blr_clk_ratio = params_channels(params) * pTAS2563->mnCh_size;
+       dev_info(pTAS2563->dev, "blr_clk_ratio: %d\n", blr_clk_ratio);
+       if(blr_clk_ratio != 0)
+               tas2563_slot_config(pTAS2563->codec, pTAS2563, blr_clk_ratio);
+
+       dev_info(pTAS2563->dev, "%s, sample rate: %d\n", __func__,
+               params_rate(params));
+
+       ret = tas2563_set_samplerate(pTAS2563, params_rate(params));
+
+       mutex_unlock(&pTAS2563->codec_lock);
+ret:
+       return ret;
+}
+
+static int tas2563_set_fmt(struct tas2563_priv *pTAS2563, unsigned int fmt)
+{
+       u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
+       int ret = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               asi_cfg_1 = 0x00;
+               break;
+       default:
+               dev_err(pTAS2563->dev, "ASI format master is not found\n");
+               ret = -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               dev_info(pTAS2563->dev, "INV format: NBNF\n");
+               asi_cfg_1 |= TAS2563_TDMConfigurationReg1_RXEDGE_Rising;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               dev_info(pTAS2563->dev, "INV format: IBNF\n");
+               asi_cfg_1 |= TAS2563_TDMConfigurationReg1_RXEDGE_Falling;
+               break;
+       default:
+               dev_err(pTAS2563->dev, "ASI format Inverse is not found\n");
+               ret = -EINVAL;
+       }
+
+       pTAS2563->update_bits(pTAS2563, TAS2563_TDMConfigurationReg1,
+               TAS2563_TDMConfigurationReg1_RXEDGE_Mask,
+               asi_cfg_1);
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case (SND_SOC_DAIFMT_I2S):
+               tdm_rx_start_slot = 1;
+               break;
+       case (SND_SOC_DAIFMT_DSP_A):
+       case (SND_SOC_DAIFMT_DSP_B):
+               tdm_rx_start_slot = 1;
+               break;
+       case (SND_SOC_DAIFMT_LEFT_J):
+               tdm_rx_start_slot = 0;
+               break;
+       default:
+       dev_err(pTAS2563->dev, "DAI Format is not found, fmt=0x%x\n", fmt);
+       ret = -EINVAL;
+               break;
+       }
+
+       pTAS2563->update_bits(pTAS2563, TAS2563_TDMConfigurationReg1,
+               TAS2563_TDMConfigurationReg1_RXOFFSET51_Mask,
+       (tdm_rx_start_slot << TAS2563_TDMConfigurationReg1_RXOFFSET51_Shift));
+
+       pTAS2563->mnASIFormat = fmt;
+
+       return 0;
+}
+
+
+static int tas2563_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       dev_info(pTAS2563->dev, "%s, format=0x%x\n", __func__, fmt);
+
+       mutex_lock(&pTAS2563->codec_lock);
+
+       ret = tas2563_set_fmt(pTAS2563, fmt);
+
+       mutex_unlock(&pTAS2563->codec_lock);
+       return ret;
+}
+
+static int tas2563_set_dai_tdm_slot(struct snd_soc_dai *dai,
+               unsigned int tx_mask, unsigned int rx_mask,
+               int slots, int slot_width)
+{
+       int ret = 0;
+       struct snd_soc_codec *codec = dai->codec;
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+
+       dev_info(pTAS2563->dev, "%s, tx_mask:%d, rx_mask:%d, slots:%d, slot_width:%d",
+                       __func__, tx_mask, rx_mask, slots, slot_width);
+
+       ret = tas2563_set_slot(pTAS2563, slot_width);
+
+       return ret;
+}
+
+static int tas2563_program_get(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+
+       mutex_lock(&pTAS2563->codec_lock);
+
+       pValue->value.integer.value[0] = pTAS2563->mnCurrentProgram;
+       dev_info(pTAS2563->dev, "tas2563_program_get = %d\n",
+               pTAS2563->mnCurrentProgram);
+
+       mutex_unlock(&pTAS2563->codec_lock);
+       return 0;
+}
+
+static int tas2563_program_put(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+       unsigned int nProgram = pValue->value.integer.value[0];
+       int ret = 0, nConfiguration = -1;
+
+       mutex_lock(&pTAS2563->codec_lock);
+
+       if (nProgram == pTAS2563->mnCurrentProgram)
+               nConfiguration = pTAS2563->mnCurrentConfiguration;
+       ret = tas2563_set_program(pTAS2563, nProgram, nConfiguration);
+
+       mutex_unlock(&pTAS2563->codec_lock);
+       return ret;
+}
+
+static int tas2563_calibration_get(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+
+       mutex_lock(&pTAS2563->codec_lock);
+
+       pValue->value.integer.value[0] = pTAS2563->mnCurrentCalibration;
+       dev_info(pTAS2563->dev,
+               "tas2563_calibration_get = %d\n",
+               pTAS2563->mnCurrentCalibration);
+
+       mutex_unlock(&pTAS2563->codec_lock);
+       return 0;
+}
+
+static int tas2563_calibration_put(struct snd_kcontrol *pKcontrol,
+       struct snd_ctl_elem_value *pValue)
+{
+#ifdef KCONTROL_CODEC
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol);
+#else
+       struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol);
+#endif
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+       unsigned int nCalibration = pValue->value.integer.value[0];
+       int ret = 0;
+
+       mutex_lock(&pTAS2563->codec_lock);
+
+       ret = tas2563_set_calibration(pTAS2563, nCalibration);
+
+       mutex_unlock(&pTAS2563->codec_lock);
+       return ret;
+}
+
+static struct snd_soc_dai_ops tas2563_dai_ops = {
+       .digital_mute = tas2563_mute,
+       .hw_params  = tas2563_hw_params,
+       .set_fmt    = tas2563_set_dai_fmt,
+       .set_tdm_slot = tas2563_set_dai_tdm_slot,
+};
+
+#define TAS2563_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+               SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TAS2563_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 \
+                                               SNDRV_PCM_RATE_88200 |\
+                                               SNDRV_PCM_RATE_96000 |\
+                                               SNDRV_PCM_RATE_176400 |\
+                                               SNDRV_PCM_RATE_192000\
+                                               )
+
+static struct snd_soc_dai_driver tas2563_dai_driver[] = {
+       {
+               .name = "tas2563 ASI1",
+               .id = 0,
+               .playback = {
+                       .stream_name    = "ASI1 Playback",
+                       .channels_min   = 2,
+                       .channels_max   = 2,
+                       .rates      = SNDRV_PCM_RATE_8000_192000,
+                       .formats    = TAS2563_FORMATS,
+               },
+               .capture = {
+                       .stream_name    = "ASI1 Capture",
+                       .channels_min   = 0,
+                       .channels_max   = 2,
+                       .rates          = SNDRV_PCM_RATE_8000_192000,
+                       .formats    = TAS2563_FORMATS,
+               },
+               .ops = &tas2563_dai_ops,
+               .symmetric_rates = 1,
+       },
+};
+
+static int tas2563_codec_probe(struct snd_soc_codec *codec)
+{
+       int ret;
+       struct tas2563_priv *pTAS2563 = snd_soc_codec_get_drvdata(codec);
+
+       ret = snd_soc_add_codec_controls(codec, tas2563_controls,
+                                        ARRAY_SIZE(tas2563_controls));
+       if (ret < 0) {
+               pr_err("%s: add_codec_controls failed, err %d\n",
+                       __func__, ret);
+               return ret;
+       }
+       pTAS2563->codec = codec;
+       pTAS2563->set_calibration = tas2563_set_calibration;
+       pTAS2563->set_config = tas2563_set_config;
+
+       dev_err(pTAS2563->dev, "%s\n", __func__);
+
+       return 0;
+}
+
+static int tas2563_codec_remove(struct snd_soc_codec *codec)
+{
+       return 0;
+}
+
+/*static DECLARE_TLV_DB_SCALE(dac_tlv, 0, 100, 0);*/
+static DECLARE_TLV_DB_SCALE(tas2563_digital_tlv, 1100, 50, 0);
+
+static const struct snd_kcontrol_new tas2563_snd_controls[] = {
+       SOC_SINGLE_TLV("Amp Output Level", TAS2563_PlaybackConfigurationReg0,
+               0, 0x16, 0,
+               tas2563_digital_tlv),
+       SOC_SINGLE_EXT("Program", SND_SOC_NOPM, 0, 0x00FF, 0, tas2563_program_get,
+               tas2563_program_put),
+       SOC_SINGLE_EXT("Configuration", SND_SOC_NOPM, 0, 0x00FF, 0,
+               tas2563_configuration_get, tas2563_configuration_put),
+       SOC_SINGLE_EXT("Calibration", SND_SOC_NOPM, 0, 0x00FF, 0,
+               tas2563_calibration_get, tas2563_calibration_put),
+};
+
+static struct snd_soc_codec_driver soc_codec_driver_tas2563 = {
+       .probe                  = tas2563_codec_probe,
+       .remove                 = tas2563_codec_remove,
+       .read                   = tas2563_codec_read,
+       .write                  = tas2563_codec_write,
+       .suspend                = tas2563_codec_suspend,
+       .resume                 = tas2563_codec_resume,
+#ifdef KCONTROL_CODEC
+       .component_driver = {
+#endif
+               .controls               = tas2563_snd_controls,
+               .num_controls           = ARRAY_SIZE(tas2563_snd_controls),
+               .dapm_widgets           = tas2563_dapm_widgets,
+               .num_dapm_widgets       = ARRAY_SIZE(tas2563_dapm_widgets),
+               .dapm_routes            = tas2563_audio_map,
+               .num_dapm_routes        = ARRAY_SIZE(tas2563_audio_map),
+#ifdef KCONTROL_CODEC
+       },
+#endif
+};
+
+int tas2563_register_codec(struct tas2563_priv *pTAS2563)
+{
+       int nResult = 0;
+
+       dev_info(pTAS2563->dev, "%s, enter\n", __func__);
+       nResult = snd_soc_register_codec(pTAS2563->dev,
+               &soc_codec_driver_tas2563,
+               tas2563_dai_driver, ARRAY_SIZE(tas2563_dai_driver));
+       return nResult;
+}
+
+int tas2563_deregister_codec(struct tas2563_priv *pTAS2563)
+{
+       snd_soc_unregister_codec(pTAS2563->dev);
+
+       return 0;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2563 ALSA SOC Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
+#endif /* CONFIG_TAS2563_CODEC */
diff --git a/tas2563-codec.h b/tas2563-codec.h
new file mode 100644 (file)
index 0000000..35d38c2
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** This program is free software; you can redistribute it and/or modify it under
+** the terms of the GNU General Public License as published by the Free Software
+** Foundation; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details.
+**
+** File:
+**     tas2563-codec.h
+**
+** Description:
+**     header file for tas2563-codec.c
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2563_CODEC_H
+#define _TAS2563_CODEC_H
+
+#include "tas2563.h"
+
+int tas2563_register_codec(struct tas2563_priv *pTAS2563);
+int tas2563_deregister_codec(struct tas2563_priv *pTAS2563);
+int tas2563_set_program(struct tas2563_priv *pTAS2563, unsigned int nProgram, int nConfig);
+int tas2563_LoadConfig(struct tas2563_priv *pTAS2563, bool bPowerOn);
+void tas2563_fw_ready(const struct firmware *pFW, void *pContext);
+int tas2563_set_config(struct tas2563_priv *pTAS2563, int config);
+
+#endif /* _TAS2563_CODEC_H */
diff --git a/tas2563-misc.c b/tas2563-misc.c
new file mode 100755 (executable)
index 0000000..8e013bb
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** This program is free software; you can redistribute it and/or modify it under
+** the terms of the GNU General Public License as published by the Free Software
+** Foundation; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** File:
+**     tas2563-misc.c
+**
+** Description:
+**     misc driver for Texas Instruments TAS2563 High Performance 4W Smart Amplifier
+**
+** =============================================================================
+*/
+
+#ifdef CONFIG_TAS2563_MISC
+
+#define DEBUG
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/fcntl.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+
+#include "tas2563.h"
+#include "tas2563-misc.h"
+#include <linux/dma-mapping.h>
+
+static int g_logEnable = 1;
+static struct tas2563_priv *g_tas2563;
+
+static int tas2563_file_open(struct inode *inode, struct file *file)
+{
+       struct tas2563_priv *pTAS2563 = g_tas2563;
+
+       if (!try_module_get(THIS_MODULE))
+               return -ENODEV;
+
+       file->private_data = (void *)pTAS2563;
+       if (g_logEnable)
+               dev_info(pTAS2563->dev, "%s\n", __func__);
+       return 0;
+}
+
+static int tas2563_file_release(struct inode *inode, struct file *file)
+{
+       struct tas2563_priv *pTAS2563 = (struct tas2563_priv *)file->private_data;
+
+       if (g_logEnable)
+               dev_info(pTAS2563->dev, "%s\n", __func__);
+       file->private_data = (void *)NULL;
+       module_put(THIS_MODULE);
+
+       return 0;
+}
+
+static ssize_t tas2563_file_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+       struct tas2563_priv *pTAS2563 = (struct tas2563_priv *)file->private_data;
+       int ret = 0;
+       unsigned int nValue = 0;
+       unsigned char value = 0;
+       unsigned char *p_kBuf = NULL;
+
+       mutex_lock(&pTAS2563->file_lock);
+
+       switch (pTAS2563->mnDBGCmd) {
+       case TIAUDIO_CMD_REG_READ: {
+               if (g_logEnable)
+                       dev_info(pTAS2563->dev, "TIAUDIO_CMD_REG_READ: current_reg = 0x%x, count=%d\n",
+                               pTAS2563->mnCurrentReg, (int)count);
+               if (count == 1) {
+                       ret = pTAS2563->read(pTAS2563, pTAS2563->mnCurrentReg, &nValue);
+                       if (ret < 0)
+                               break;
+
+                       value = (u8)nValue;
+                       if (g_logEnable)
+                               dev_info(pTAS2563->dev, "TIAUDIO_CMD_REG_READ: nValue=0x%x, value=0x%x\n", nValue, value);
+                       ret = copy_to_user(buf, &value, 1);
+                       if (ret != 0) {
+                               /* Failed to copy all the data, exit */
+                               dev_err(pTAS2563->dev, "copy to user fail %d\n", ret);
+                       }
+               } else if (count > 1) {
+                       p_kBuf = kzalloc(count, GFP_KERNEL);
+                       if (p_kBuf != NULL) {
+                               ret = pTAS2563->bulk_read(pTAS2563, pTAS2563->mnCurrentReg, p_kBuf, count);
+                               if (ret < 0)
+                                       break;
+                               ret = copy_to_user(buf, p_kBuf, count);
+                               if (ret != 0) {
+                                       /* Failed to copy all the data, exit */
+                                       dev_err(pTAS2563->dev, "copy to user fail %d\n", ret);
+                               }
+                               kfree(p_kBuf);
+                       } else
+                               dev_err(pTAS2563->dev, "read no mem\n");
+               }
+       }
+       break;
+
+       }
+       pTAS2563->mnDBGCmd = 0;
+
+       mutex_unlock(&pTAS2563->file_lock);
+       return count;
+}
+
+static ssize_t tas2563_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+       struct tas2563_priv *pTAS2563 = (struct tas2563_priv *)file->private_data;
+       int ret = 0;
+       unsigned char *p_kBuf = NULL;
+       unsigned int reg = 0;
+       unsigned int len = 0;
+
+       mutex_lock(&pTAS2563->file_lock);
+
+       p_kBuf = kzalloc(count, GFP_KERNEL);
+       if (p_kBuf == NULL) {
+               dev_err(pTAS2563->dev, "write no mem\n");
+               goto err;
+       }
+
+       ret = copy_from_user(p_kBuf, buf, count);
+       if (ret != 0) {
+               dev_err(pTAS2563->dev, "copy_from_user failed.\n");
+               goto err;
+       }
+
+       pTAS2563->mnDBGCmd = p_kBuf[0];
+       switch (pTAS2563->mnDBGCmd) {
+       case TIAUDIO_CMD_REG_WITE:
+               if (count > 5) {
+                       reg = ((unsigned int)p_kBuf[1] << 24)
+                               + ((unsigned int)p_kBuf[2] << 16)
+                               + ((unsigned int)p_kBuf[3] << 8)
+                               + (unsigned int)p_kBuf[4];
+                       len = count - 5;
+                       if (len == 1) {
+                               ret = pTAS2563->write(pTAS2563, reg, p_kBuf[5]);
+                               if (g_logEnable)
+                                       dev_info(pTAS2563->dev, "TIAUDIO_CMD_REG_WITE, Reg=0x%x, Val=0x%x\n", reg, p_kBuf[5]);
+                       } else
+                               ret = pTAS2563->bulk_write(pTAS2563, reg, &p_kBuf[5], len);
+               } else
+                       dev_err(pTAS2563->dev, "%s, write len fail, count=%d.\n", __func__, (int)count);
+               pTAS2563->mnDBGCmd = 0;
+       break;
+
+       case TIAUDIO_CMD_REG_READ:
+               if (count == 5) {
+                       pTAS2563->mnCurrentReg = ((unsigned int)p_kBuf[1] << 24)
+                               + ((unsigned int)p_kBuf[2] << 16)
+                               + ((unsigned int)p_kBuf[3] << 8)
+                               + (unsigned int)p_kBuf[4];
+                       if (g_logEnable)
+                               dev_info(pTAS2563->dev, "TIAUDIO_CMD_REG_READ whole=0x%x\n", pTAS2563->mnCurrentReg);
+               } else
+                       dev_err(pTAS2563->dev, "read len fail.\n");
+       break;
+       }
+
+err:
+       if (p_kBuf != NULL)
+               kfree(p_kBuf);
+
+       mutex_unlock(&pTAS2563->file_lock);
+
+       return count;
+}
+
+static const struct file_operations fops = {
+       .owner = THIS_MODULE,
+       .read = tas2563_file_read,
+       .write = tas2563_file_write,
+       .unlocked_ioctl = NULL,
+       .open = tas2563_file_open,
+       .release = tas2563_file_release,
+};
+
+#define MODULE_NAME    "tas2563"
+static struct miscdevice tas2563_misc = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = MODULE_NAME,
+       .fops = &fops,
+};
+
+int tas2563_register_misc(struct tas2563_priv *pTAS2563)
+{
+       int ret = 0;
+
+       g_tas2563 = pTAS2563;
+
+       ret = misc_register(&tas2563_misc);
+       if (ret)
+               dev_err(pTAS2563->dev, "TAS2563 misc fail: %d\n", ret);
+
+       dev_info(pTAS2563->dev, "%s, leave\n", __func__);
+
+       return ret;
+}
+
+int tas2563_deregister_misc(struct tas2563_priv *pTAS2563)
+{
+       misc_deregister(&tas2563_misc);
+       return 0;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2563 Misc Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
+#endif
diff --git a/tas2563-misc.h b/tas2563-misc.h
new file mode 100755 (executable)
index 0000000..2ca10f1
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** This program is free software; you can redistribute it and/or modify it under
+** the terms of the GNU General Public License as published by the Free Software
+** Foundation; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** File:
+**     tas2563-misc.h
+**
+** Description:
+**     header file for tas2563-misc.c
+**
+** =============================================================================
+*/
+
+#ifndef _TAS2563_MISC_H
+#define _TAS2563_MISC_H
+
+#define        FW_NAME_SIZE                    64
+#define        FW_DESCRIPTION_SIZE             256
+#define        PROGRAM_BUF_SIZE                (5 + FW_NAME_SIZE + FW_DESCRIPTION_SIZE)
+#define        CONFIGURATION_BUF_SIZE  (8 + FW_NAME_SIZE + FW_DESCRIPTION_SIZE)
+
+#define        TIAUDIO_CMD_REG_WITE                    1
+#define        TIAUDIO_CMD_REG_READ                    2
+#define        TIAUDIO_CMD_DEBUG_ON                    3
+#define        TIAUDIO_CMD_PROGRAM                             4
+#define        TIAUDIO_CMD_CONFIGURATION               5
+#define        TIAUDIO_CMD_FW_TIMESTAMP                6
+#define        TIAUDIO_CMD_CALIBRATION                 7
+#define        TIAUDIO_CMD_SAMPLERATE                  8
+#define        TIAUDIO_CMD_BITRATE                             9
+#define        TIAUDIO_CMD_DACVOLUME                   10
+#define        TIAUDIO_CMD_SPEAKER                             11
+#define        TIAUDIO_CMD_FW_RELOAD                   12
+
+#define        TAS2563_MAGIC_NUMBER    0x32353537      /* '2563' */
+
+#define        SMARTPA_SPK_DAC_VOLUME                          _IOWR(TAS2563_MAGIC_NUMBER, 1, unsigned long)
+#define        SMARTPA_SPK_POWER_ON                            _IOWR(TAS2563_MAGIC_NUMBER, 2, unsigned long)
+#define        SMARTPA_SPK_POWER_OFF                           _IOWR(TAS2563_MAGIC_NUMBER, 3, unsigned long)
+#define        SMARTPA_SPK_SWITCH_PROGRAM                      _IOWR(TAS2563_MAGIC_NUMBER, 4, unsigned long)
+#define        SMARTPA_SPK_SWITCH_CONFIGURATION        _IOWR(TAS2563_MAGIC_NUMBER, 5, unsigned long)
+#define        SMARTPA_SPK_SWITCH_CALIBRATION          _IOWR(TAS2563_MAGIC_NUMBER, 6, unsigned long)
+#define        SMARTPA_SPK_SET_SAMPLERATE                      _IOWR(TAS2563_MAGIC_NUMBER, 7, unsigned long)
+#define        SMARTPA_SPK_SET_BITRATE                         _IOWR(TAS2563_MAGIC_NUMBER, 8, unsigned long)
+
+int tas2563_register_misc(struct tas2563_priv *pTAS2563);
+int tas2563_deregister_misc(struct tas2563_priv *pTAS2563);
+
+#endif /* _TAS2563_MISC_H */
diff --git a/tas2563-regmap.c b/tas2563-regmap.c
new file mode 100755 (executable)
index 0000000..a9c580f
--- /dev/null
@@ -0,0 +1,793 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** This program is free software; you can redistribute it and/or modify it under
+** the terms of the GNU General Public License as published by the Free Software
+** Foundation; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** File:
+**     tas2563-regmap.c
+**
+** Description:
+**     I2C driver with regmap for Texas Instruments TAS2563 High Performance 4W Smart Amplifier
+**
+** =============================================================================
+*/
+
+#ifdef CONFIG_TAS2563_REGMAP
+
+#define DEBUG
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/fcntl.h>
+#include <linux/uaccess.h>
+#include "tas2563.h"
+
+#ifdef CONFIG_TAS2563_CODEC
+#include "tas2563-codec.h"
+#endif
+
+#ifdef CONFIG_TAS2563_MISC
+#include "tas2563-misc.h"
+#endif
+
+#define ENABLE_TILOAD
+#ifdef ENABLE_TILOAD
+#include "tiload.h"
+#endif
+
+#define LOW_TEMPERATURE_GAIN 6
+#define LOW_TEMPERATURE_COUNTER 12
+
+static int tas2563_change_book_page(struct tas2563_priv *pTAS2563,
+       int book, int page)
+{
+       int nResult = 0;
+       dev_dbg(pTAS2563->dev, "%s, %d", __func__, __LINE__);
+
+       if ((pTAS2563->mnCurrentBook == book)
+               && (pTAS2563->mnCurrentPage == page))
+               goto end;
+
+       if (pTAS2563->mnCurrentBook != book) {
+       nResult = regmap_write(pTAS2563->regmap, TAS2563_BOOKCTL_PAGE, 0);
+               if (nResult < 0) {
+                       dev_err(pTAS2563->dev, "%s, ERROR, L=%d, E=%d\n",
+                               __func__, __LINE__, nResult);
+                       goto end;
+               }
+       pTAS2563->mnCurrentPage = 0;
+       nResult = regmap_write(pTAS2563->regmap, TAS2563_BOOKCTL_REG, book);
+               if (nResult < 0) {
+                       dev_err(pTAS2563->dev, "%s, ERROR, L=%d, E=%d\n",
+                               __func__, __LINE__, nResult);
+                       goto end;
+               }
+               pTAS2563->mnCurrentBook = book;
+       }
+
+       if (pTAS2563->mnCurrentPage != page) {
+       nResult = regmap_write(pTAS2563->regmap, TAS2563_BOOKCTL_PAGE, page);
+               if (nResult < 0) {
+                       dev_err(pTAS2563->dev, "%s, ERROR, L=%d, E=%d\n",
+                               __func__, __LINE__, nResult);
+                       goto end;
+               }
+               pTAS2563->mnCurrentPage = page;
+       }
+
+end:
+       return nResult;
+}
+
+static int tas2563_dev_read(struct tas2563_priv *pTAS2563,
+       unsigned int reg, unsigned int *pValue)
+{
+       int nResult = 0;
+
+       mutex_lock(&pTAS2563->dev_lock);
+
+       nResult = tas2563_change_book_page(pTAS2563,
+               TAS2563_BOOK_ID(reg), TAS2563_PAGE_ID(reg));
+       if (nResult < 0)
+               goto end;
+
+       nResult = regmap_read(pTAS2563->regmap, TAS2563_PAGE_REG(reg), pValue);
+       if (nResult < 0)
+               dev_err(pTAS2563->dev, "%s, ERROR, L=%d, E=%d\n",
+                       __func__, __LINE__, nResult);
+       else
+               dev_dbg(pTAS2563->dev, "%s: BOOK:PAGE:REG %u:%u:%u\n", __func__,
+                       TAS2563_BOOK_ID(reg), TAS2563_PAGE_ID(reg),
+                       TAS2563_PAGE_REG(reg));
+
+end:
+       mutex_unlock(&pTAS2563->dev_lock);
+       return nResult;
+}
+
+static int tas2563_dev_write(struct tas2563_priv *pTAS2563,
+       unsigned int reg, unsigned int value)
+{
+       int nResult = 0;
+
+       mutex_lock(&pTAS2563->dev_lock);
+
+       nResult = tas2563_change_book_page(pTAS2563,
+               TAS2563_BOOK_ID(reg), TAS2563_PAGE_ID(reg));
+       if (nResult < 0)
+               goto end;
+
+       nResult = regmap_write(pTAS2563->regmap, TAS2563_PAGE_REG(reg),
+                       value);
+       if (nResult < 0)
+               dev_err(pTAS2563->dev, "%s, ERROR, L=%d, E=%d\n",
+                       __func__, __LINE__, nResult);
+       else
+               dev_dbg(pTAS2563->dev, "%s: BOOK:PAGE:REG %u:%u:%u, VAL: 0x%02x\n",
+                       __func__, TAS2563_BOOK_ID(reg), TAS2563_PAGE_ID(reg),
+                       TAS2563_PAGE_REG(reg), value);
+
+end:
+       mutex_unlock(&pTAS2563->dev_lock);
+       return nResult;
+}
+
+static int tas2563_dev_bulk_write(struct tas2563_priv *pTAS2563,
+       unsigned int reg, u8 *pData, unsigned int nLength)
+{
+       int nResult = 0;
+
+       mutex_lock(&pTAS2563->dev_lock);
+
+       nResult = tas2563_change_book_page(pTAS2563,
+               TAS2563_BOOK_ID(reg), TAS2563_PAGE_ID(reg));
+       if (nResult < 0)
+               goto end;
+
+       nResult = regmap_bulk_write(pTAS2563->regmap,
+               TAS2563_PAGE_REG(reg), pData, nLength);
+       if (nResult < 0)
+               dev_err(pTAS2563->dev, "%s, ERROR, L=%d, E=%d\n",
+                       __func__, __LINE__, nResult);
+       else
+               dev_dbg(pTAS2563->dev, "%s: BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n",
+                       __func__, TAS2563_BOOK_ID(reg), TAS2563_PAGE_ID(reg),
+                       TAS2563_PAGE_REG(reg), nLength);
+
+end:
+       mutex_unlock(&pTAS2563->dev_lock);
+       return nResult;
+}
+
+static int tas2563_dev_bulk_read(struct tas2563_priv *pTAS2563,
+       unsigned int reg, u8 *pData, unsigned int nLength)
+{
+       int nResult = 0;
+       int i = 0;
+
+       mutex_lock(&pTAS2563->dev_lock);
+
+       nResult = tas2563_change_book_page(pTAS2563,
+               TAS2563_BOOK_ID(reg), TAS2563_PAGE_ID(reg));
+       if (nResult < 0)
+               goto end;
+
+       dev_dbg(pTAS2563->dev, "reg = %u, pData = %s, Lenth = %d", reg, pData, nLength);
+
+       #define STRIDE 4
+       /* Read chunk bytes defined by STRIDE */
+       for (i = 0; i < (nLength / STRIDE); i++) {
+                       nResult = regmap_bulk_read(pTAS2563->regmap,
+                                                       TAS2563_PAGE_REG((reg + i*STRIDE)),
+                                                       &pData[i*STRIDE], STRIDE);
+                       if (nResult < 0) {
+                                       dev_err(pTAS2563->dev, "%s, %d, I2C error %d\n",
+                                                       __func__, __LINE__, nResult);
+                                       pTAS2563->mnErrCode |= ERROR_DEVA_I2C_COMM;
+                       } else
+                                       pTAS2563->mnErrCode &= ~ERROR_DEVA_I2C_COMM;
+       }
+
+       /* Read remaining bytes */
+       if ((nLength % STRIDE) != 0) {
+                       nResult = regmap_bulk_read(pTAS2563->regmap,
+                                                       TAS2563_PAGE_REG(reg + i*STRIDE),
+                                                       &pData[i*STRIDE], (nLength % STRIDE));
+                       if (nResult < 0) {
+                                       dev_err(pTAS2563->dev, "%s, %d, I2C error %d\n",
+                                                       __func__, __LINE__, nResult);
+                                       pTAS2563->mnErrCode |= ERROR_DEVA_I2C_COMM;
+                       } else
+                                       pTAS2563->mnErrCode &= ~ERROR_DEVA_I2C_COMM;
+       }
+
+
+end:
+       mutex_unlock(&pTAS2563->dev_lock);
+       return nResult;
+}
+
+static int tas2563_dev_update_bits(struct tas2563_priv *pTAS2563,
+       unsigned int reg, unsigned int mask, unsigned int value)
+{
+       int nResult = 0;
+
+       mutex_lock(&pTAS2563->dev_lock);
+       nResult = tas2563_change_book_page(pTAS2563,
+               TAS2563_BOOK_ID(reg), TAS2563_PAGE_ID(reg));
+       if (nResult < 0)
+               goto end;
+
+       nResult = regmap_update_bits(pTAS2563->regmap,
+       TAS2563_PAGE_REG(reg), mask, value);
+       if (nResult < 0)
+               dev_err(pTAS2563->dev, "%s, ERROR, L=%d, E=%d\n",
+                       __func__, __LINE__, nResult);
+       else
+               dev_dbg(pTAS2563->dev, "%s: BOOK:PAGE:REG %u:%u:%u, mask: 0x%x, val=0x%x\n",
+                       __func__, TAS2563_BOOK_ID(reg), TAS2563_PAGE_ID(reg),
+                       TAS2563_PAGE_REG(reg), mask, value);
+end:
+       mutex_unlock(&pTAS2563->dev_lock);
+       return nResult;
+}
+
+static const struct reg_default tas2563_reg_defaults[] = {
+       { TAS2563_Page, 0x00 },
+       { TAS2563_SoftwareReset, 0x00 },
+       { TAS2563_PowerControl, 0x0e },
+       { TAS2563_PlaybackConfigurationReg0, 0x10 },
+       { TAS2563_MiscConfigurationReg0, 0x07 },
+       { TAS2563_TDMConfigurationReg1, 0x02 },
+       { TAS2563_TDMConfigurationReg2, 0x0a },
+       { TAS2563_TDMConfigurationReg3, 0x10 },
+       { TAS2563_InterruptMaskReg0, 0xfc },
+       { TAS2563_InterruptMaskReg1, 0xb1 },
+       { TAS2563_InterruptConfiguration, 0x05 },
+       { TAS2563_MiscIRQ, 0x81 },
+       { TAS2563_ClockConfiguration, 0x0c },
+
+};
+
+static bool tas2563_volatile(struct device *dev, unsigned int reg)
+{
+       return true;
+}
+
+static bool tas2563_writeable(struct device *dev, unsigned int reg)
+{
+       return true;
+}
+static const struct regmap_config tas2563_i2c_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .writeable_reg = tas2563_writeable,
+       .volatile_reg = tas2563_volatile,
+//     .reg_defaults = tas2563_reg_defaults,
+//     .num_reg_defaults = ARRAY_SIZE(tas2563_reg_defaults),
+       .cache_type = REGCACHE_NONE,
+       .max_register = 1 * 128,
+};
+
+
+static void tas2563_hw_reset(struct tas2563_priv *pTAS2563)
+{
+       if (gpio_is_valid(pTAS2563->mnResetGPIO)) {
+               gpio_direction_output(pTAS2563->mnResetGPIO, 0);
+               msleep(5);
+               gpio_direction_output(pTAS2563->mnResetGPIO, 1);
+               msleep(2);
+       }
+
+       pTAS2563->mnCurrentBook = -1;
+       pTAS2563->mnCurrentPage = -1;
+}
+
+void tas2563_enableIRQ(struct tas2563_priv *pTAS2563, bool enable)
+{
+       if (enable) {
+               if (pTAS2563->mbIRQEnable)
+                       return;
+
+               if (gpio_is_valid(pTAS2563->mnIRQGPIO))
+                       enable_irq(pTAS2563->mnIRQ);
+
+               schedule_delayed_work(&pTAS2563->irq_work, msecs_to_jiffies(10));
+               pTAS2563->mbIRQEnable = true;
+       } else {
+               if (gpio_is_valid(pTAS2563->mnIRQGPIO))
+                       disable_irq_nosync(pTAS2563->mnIRQ);
+               pTAS2563->mbIRQEnable = false;
+       }
+}
+
+
+static void irq_work_routine(struct work_struct *work)
+{
+       struct tas2563_priv *pTAS2563 =
+               container_of(work, struct tas2563_priv, irq_work.work);
+       unsigned int nDevInt1Status = 0, nDevInt2Status = 0;
+       int nCounter = 2;
+       int nResult = 0;
+
+       dev_info(pTAS2563->dev, "%s\n", __func__);
+#ifdef CONFIG_TAS2563_CODEC
+       mutex_lock(&pTAS2563->codec_lock);
+#endif
+
+       if (pTAS2563->mbRuntimeSuspend) {
+               dev_info(pTAS2563->dev, "%s, Runtime Suspended\n", __func__);
+               goto end;
+       }
+
+       if (pTAS2563->mnPowerState == TAS2563_POWER_SHUTDOWN) {
+               dev_info(pTAS2563->dev, "%s, device not powered\n", __func__);
+               goto end;
+       }
+
+       nResult = regmap_write(pTAS2563->regmap, TAS2563_InterruptMaskReg0,
+                               TAS2563_InterruptMaskReg0_Disable);
+       nResult = regmap_write(pTAS2563->regmap, TAS2563_InterruptMaskReg1,
+                               TAS2563_InterruptMaskReg1_Disable);
+
+       if (nResult < 0)
+               goto reload;
+
+       nResult = regmap_read(pTAS2563->regmap, TAS2563_LatchedInterruptReg0, &nDevInt1Status);
+       if (nResult >= 0)
+               nResult = regmap_read(pTAS2563->regmap, TAS2563_LatchedInterruptReg1, &nDevInt2Status);
+       else
+               goto reload;
+
+       dev_dbg(pTAS2563->dev, "IRQ status : 0x%x, 0x%x\n",
+                       nDevInt1Status, nDevInt2Status);
+
+       if (((nDevInt1Status & 0x3) != 0) || ((nDevInt2Status & 0x0f) != 0)) {
+               /* in case of INT_OC, INT_OT, INT_OVLT, INT_UVLT, INT_BO */
+
+               if (nDevInt1Status & TAS2563_LatchedInterruptReg0_OCEFlagSticky_Interrupt) {
+                       pTAS2563->mnErrCode |= ERROR_OVER_CURRENT;
+                       dev_err(pTAS2563->dev, "SPK over current!\n");
+               } else
+                       pTAS2563->mnErrCode &= ~ERROR_OVER_CURRENT;
+
+               if (nDevInt1Status & TAS2563_LatchedInterruptReg0_OTEFlagSticky_Interrupt) {
+                       pTAS2563->mnErrCode |= ERROR_DIE_OVERTEMP;
+                       dev_err(pTAS2563->dev, "die over temperature!\n");
+               } else
+                       pTAS2563->mnErrCode &= ~ERROR_DIE_OVERTEMP;
+
+               if (nDevInt2Status & TAS2563_LatchedInterruptReg1_VBATOVLOSticky_Interrupt) {
+                       pTAS2563->mnErrCode |= ERROR_OVER_VOLTAGE;
+                       dev_err(pTAS2563->dev, "SPK over voltage!\n");
+               } else
+                       pTAS2563->mnErrCode &= ~ERROR_UNDER_VOLTAGE;
+
+               if (nDevInt2Status & TAS2563_LatchedInterruptReg1_VBATUVLOSticky_Interrupt) {
+                       pTAS2563->mnErrCode |= ERROR_UNDER_VOLTAGE;
+                       dev_err(pTAS2563->dev, "SPK under voltage!\n");
+               } else
+                       pTAS2563->mnErrCode &= ~ERROR_UNDER_VOLTAGE;
+
+               if (nDevInt2Status & TAS2563_LatchedInterruptReg1_BrownOutFlagSticky_Interrupt) {
+                       pTAS2563->mnErrCode |= ERROR_BROWNOUT;
+                       dev_err(pTAS2563->dev, "brownout!\n");
+               } else
+                       pTAS2563->mnErrCode &= ~ERROR_BROWNOUT;
+
+               goto reload;
+       } else {
+               nCounter = 2;
+
+               while (nCounter > 0) {
+                       nResult = regmap_read(pTAS2563->regmap, TAS2563_PowerControl, &nDevInt1Status);
+                       if (nResult < 0)
+                               goto reload;
+
+                       if ((nDevInt1Status & TAS2563_PowerControl_OperationalMode10_Mask)
+                               != TAS2563_PowerControl_OperationalMode10_Shutdown)
+                               break;
+
+                       nCounter--;
+                       if (nCounter > 0) {
+                               /* in case check pow status just after power on TAS2563 */
+                               dev_dbg(pTAS2563->dev, "PowSts B: 0x%x, check again after 10ms\n",
+                                       nDevInt1Status);
+                               msleep(10);
+                       }
+               }
+
+               if ((nDevInt1Status & TAS2563_PowerControl_OperationalMode10_Mask)
+                       == TAS2563_PowerControl_OperationalMode10_Shutdown) {
+                       dev_err(pTAS2563->dev, "%s, Critical ERROR REG[0x%x] = 0x%x\n",
+                               __func__,
+                               TAS2563_PowerControl,
+                               nDevInt1Status);
+                       pTAS2563->mnErrCode |= ERROR_CLASSD_PWR;
+                       goto reload;
+               }
+               pTAS2563->mnErrCode &= ~ERROR_CLASSD_PWR;
+       }
+
+       nResult = regmap_write(pTAS2563->regmap, TAS2563_InterruptMaskReg0, 0xfc);
+       if (nResult < 0)
+               goto reload;
+
+       nResult = regmap_write(pTAS2563->regmap, TAS2563_InterruptMaskReg1, 0xb1);
+       if (nResult < 0)
+               goto reload;
+
+       goto end;
+
+reload:
+       /* hardware reset and reload */
+       nResult = -1;
+       //tas2563_LoadConfig(pTAS2563);
+       tas2563_set_program(pTAS2563, pTAS2563->mnCurrentProgram, pTAS2563->mnCurrentConfiguration);
+
+       if (nResult >= 0) {
+               tas2563_enableIRQ(pTAS2563, true);
+       }
+       
+end:
+#ifdef CONFIG_TAS2563_CODEC
+       mutex_unlock(&pTAS2563->codec_lock);
+#endif
+}
+
+static enum hrtimer_restart timer_func(struct hrtimer *timer)
+{
+       struct tas2563_priv *pTAS2563 = container_of(timer,
+               struct tas2563_priv, mtimerwork);
+
+       if (pTAS2563->mnPowerState != TAS2563_POWER_SHUTDOWN) {
+               if (!delayed_work_pending(&pTAS2563->irq_work))
+                       schedule_delayed_work(&pTAS2563->irq_work,
+                               msecs_to_jiffies(20));
+       }
+
+       return HRTIMER_NORESTART;
+}
+
+static irqreturn_t tas2563_irq_handler(int irq, void *dev_id)
+{
+       struct tas2563_priv *pTAS2563 = (struct tas2563_priv *)dev_id;
+
+       tas2563_enableIRQ(pTAS2563, false);
+       /* get IRQ status after 100 ms */
+       schedule_delayed_work(&pTAS2563->irq_work, msecs_to_jiffies(100));
+       return IRQ_HANDLED;
+}
+
+
+static int tas2563_parse_dt(struct device *dev, struct tas2563_priv *pTAS2563)
+{
+       struct device_node *np = dev->of_node;
+       int rc = 0, ret = 0;
+
+       rc = of_property_read_u32(np, "ti,asi-format", &pTAS2563->mnASIFormat);
+       if (rc) {
+               dev_err(pTAS2563->dev, "Looking up %s property in node %s failed %d\n",
+                       "ti,asi-format", np->full_name, rc);
+       } else {
+               dev_dbg(pTAS2563->dev, "ti,asi-format=%d",
+                       pTAS2563->mnASIFormat);
+       }
+
+       pTAS2563->mnResetGPIO = of_get_named_gpio(np, "ti,reset-gpio", 0);
+       if (!gpio_is_valid(pTAS2563->mnResetGPIO)) {
+               dev_err(pTAS2563->dev, "Looking up %s property in node %s failed %d\n",
+                       "ti,reset-gpio", np->full_name, pTAS2563->mnResetGPIO);
+       } else {
+               dev_dbg(pTAS2563->dev, "ti,reset-gpio=%d",
+                       pTAS2563->mnResetGPIO);
+       }
+
+       pTAS2563->mnIRQGPIO = of_get_named_gpio(np, "ti,irq-gpio", 0);
+       if (!gpio_is_valid(pTAS2563->mnIRQGPIO)) {
+               dev_err(pTAS2563->dev, "Looking up %s property in node %s failed %d\n",
+                       "ti,irq-gpio", np->full_name, pTAS2563->mnIRQGPIO);
+       } else {
+               dev_dbg(pTAS2563->dev, "ti,irq-gpio=%d", pTAS2563->mnIRQGPIO);
+       }
+
+       of_property_read_u32(np, "ti,left-slot", &pTAS2563->mnLeftSlot);
+       if (rc) {
+               dev_err(pTAS2563->dev, "Looking up %s property in node %s failed %d\n",
+                       "ti,left-slot", np->full_name, rc);
+       } else {
+               dev_dbg(pTAS2563->dev, "ti,left-slot=%d",
+                       pTAS2563->mnLeftSlot);
+       }
+
+       of_property_read_u32(np, "ti,right-slot", &pTAS2563->mnRightSlot);
+       if (rc) {
+               dev_err(pTAS2563->dev, "Looking up %s property in node %s failed %d\n",
+                       "ti,right-slot", np->full_name, rc);
+       } else {
+               dev_dbg(pTAS2563->dev, "ti,right-slot=%d",
+                       pTAS2563->mnRightSlot);
+       }
+
+       of_property_read_u32(np, "ti,imon-slot-no", &pTAS2563->mnImon_slot_no);
+       if (rc) {
+               dev_err(pTAS2563->dev, "Looking up %s property in node %s failed %d\n",
+                       "ti,imon-slot-no", np->full_name, rc);
+       } else {
+               dev_dbg(pTAS2563->dev, "ti,imon-slot-no=%d",
+                       pTAS2563->mnImon_slot_no);
+       }
+
+       of_property_read_u32(np, "ti,vmon-slot-no", &pTAS2563->mnVmon_slot_no);
+       if (rc) {
+               dev_err(pTAS2563->dev, "Looking up %s property in node %s failed %d\n",
+                       "ti,vmon-slot-no", np->full_name, rc);
+       } else {
+               dev_dbg(pTAS2563->dev, "ti,vmon-slot-no=%d",
+                       pTAS2563->mnVmon_slot_no);
+       }
+
+       return ret;
+}
+
+
+static int tas2563_runtime_suspend(struct tas2563_priv *pTAS2563)
+{
+       dev_dbg(pTAS2563->dev, "%s\n", __func__);
+
+       pTAS2563->mbRuntimeSuspend = true;
+
+       if (gpio_is_valid(pTAS2563->mnIRQGPIO)) {
+               if (delayed_work_pending(&pTAS2563->irq_work)) {
+                       dev_dbg(pTAS2563->dev, "cancel IRQ work\n");
+                       cancel_delayed_work_sync(&pTAS2563->irq_work);
+               }
+       }
+
+       return 0;
+}
+
+static int tas2563_runtime_resume(struct tas2563_priv *pTAS2563)
+{
+       struct TProgram *pProgram;
+
+       dev_dbg(pTAS2563->dev, "%s\n", __func__);
+       if (!pTAS2563->mpFirmware->mpPrograms) {
+               dev_dbg(pTAS2563->dev, "%s, firmware not loaded\n", __func__);
+               goto end;
+       }
+
+       if (pTAS2563->mnCurrentProgram >= pTAS2563->mpFirmware->mnPrograms) {
+               dev_err(pTAS2563->dev, "%s, firmware corrupted\n", __func__);
+               goto end;
+       }
+
+       pProgram = &(pTAS2563->mpFirmware->mpPrograms[pTAS2563->mnCurrentProgram]);
+
+       pTAS2563->mbRuntimeSuspend = false;
+end:
+
+       return 0;
+}
+
+
+/* tas2563_i2c_probe :
+* platform dependent
+* should implement hardware reset functionality
+*/
+static int tas2563_i2c_probe(struct i2c_client *pClient,
+       const struct i2c_device_id *pID)
+{
+       struct tas2563_priv *pTAS2563;
+       int nResult = 0;
+       unsigned int nValue = 0;
+       const char *pFWName;
+
+       dev_info(&pClient->dev, "%s enter\n", __func__);
+
+       pTAS2563 = devm_kzalloc(&pClient->dev, sizeof(struct tas2563_priv), GFP_KERNEL);
+       if (!pTAS2563) {
+               nResult = -ENOMEM;
+               goto err;
+       }
+
+       pTAS2563->dev = &pClient->dev;
+       i2c_set_clientdata(pClient, pTAS2563);
+       dev_set_drvdata(&pClient->dev, pTAS2563);
+
+       pTAS2563->regmap = devm_regmap_init_i2c(pClient, &tas2563_i2c_regmap);
+       if (IS_ERR(pTAS2563->regmap)) {
+               nResult = PTR_ERR(pTAS2563->regmap);
+               dev_err(&pClient->dev, "Failed to allocate register map: %d\n",
+                       nResult);
+               goto err;
+       }
+
+       if (pClient->dev.of_node)
+               tas2563_parse_dt(&pClient->dev, pTAS2563);
+
+       if (gpio_is_valid(pTAS2563->mnResetGPIO)) {
+               nResult = gpio_request(pTAS2563->mnResetGPIO, "TAS2563-RESET");
+               if (nResult < 0) {
+                       dev_err(pTAS2563->dev, "%s: GPIO %d request error\n",
+                               __func__, pTAS2563->mnResetGPIO);
+                       goto err;
+               }
+               tas2563_hw_reset(pTAS2563);
+       }
+
+       pTAS2563->read = tas2563_dev_read;
+       pTAS2563->write = tas2563_dev_write;
+       pTAS2563->bulk_read = tas2563_dev_bulk_read;
+       pTAS2563->bulk_write = tas2563_dev_bulk_write;
+       pTAS2563->update_bits = tas2563_dev_update_bits;
+       pTAS2563->enableIRQ = tas2563_enableIRQ;
+//     pTAS2563->clearIRQ = tas2563_clearIRQ;
+       pTAS2563->hw_reset = tas2563_hw_reset;
+       pTAS2563->runtime_suspend = tas2563_runtime_suspend;
+       pTAS2563->runtime_resume = tas2563_runtime_resume;
+       pTAS2563->mnRestart = 0;
+       pTAS2563->mnPowerState = TAS2563_POWER_SHUTDOWN;
+
+       mutex_init(&pTAS2563->dev_lock);
+
+       /* Reset the chip */
+       nResult = tas2563_dev_write(pTAS2563, TAS2563_SoftwareReset, 0x01);
+       if (nResult < 0) {
+               dev_err(&pClient->dev, "I2c fail, %d\n", nResult);
+//             goto err;
+       }
+
+       msleep(1);
+       nResult = tas2563_dev_read(pTAS2563, TAS2563_RevisionandPGID, &nValue);
+       pTAS2563->mnPGID = nValue;
+       dev_info(pTAS2563->dev, "PGID: %d\n", pTAS2563->mnPGID);
+       pFWName = TAS2563_FW_NAME;
+
+       if (gpio_is_valid(pTAS2563->mnIRQGPIO)) {
+               nResult = gpio_request(pTAS2563->mnIRQGPIO, "TAS2563-IRQ");
+               if (nResult < 0) {
+                       dev_err(pTAS2563->dev,
+                               "%s: GPIO %d request INT error\n",
+                               __func__, pTAS2563->mnIRQGPIO);
+                       goto err;
+               }
+
+               gpio_direction_input(pTAS2563->mnIRQGPIO);
+               pTAS2563->mnIRQ = gpio_to_irq(pTAS2563->mnIRQGPIO);
+               dev_dbg(pTAS2563->dev, "irq = %d\n", pTAS2563->mnIRQ);
+               INIT_DELAYED_WORK(&pTAS2563->irq_work, irq_work_routine);
+               nResult = request_threaded_irq(pTAS2563->mnIRQ, tas2563_irq_handler,
+                                       NULL, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+                                       pClient->name, pTAS2563);
+               if (nResult < 0) {
+                       dev_err(pTAS2563->dev,
+                               "request_irq failed, %d\n", nResult);
+                       goto err;
+               }
+               disable_irq_nosync(pTAS2563->mnIRQ);
+       }
+
+       pTAS2563->mpFirmware = devm_kzalloc(&pClient->dev, sizeof(struct TFirmware), GFP_KERNEL);
+       if (!pTAS2563->mpFirmware) {
+               nResult = -ENOMEM;
+               goto err;
+       }
+
+       pTAS2563->mpCalFirmware = devm_kzalloc(&pClient->dev, sizeof(struct TFirmware), GFP_KERNEL);
+       if (!pTAS2563->mpCalFirmware) {
+               nResult = -ENOMEM;
+               goto err;
+       }
+
+#ifdef CONFIG_TAS2563_CODEC
+       mutex_init(&pTAS2563->codec_lock);
+       nResult = tas2563_register_codec(pTAS2563);
+       if (nResult < 0) {
+               dev_err(pTAS2563->dev,
+                       "register codec failed, %d\n", nResult);
+               goto err;
+       }
+#endif
+
+#ifdef CONFIG_TAS2563_MISC
+       mutex_init(&pTAS2563->file_lock);
+       nResult = tas2563_register_misc(pTAS2563);
+       if (nResult < 0) {
+               dev_err(pTAS2563->dev,
+                       "register codec failed, %d\n", nResult);
+               goto err;
+       }
+#endif
+
+#ifdef ENABLE_TILOAD
+       tiload_driver_init(pTAS2563);
+#endif
+
+       hrtimer_init(&pTAS2563->mtimerwork, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       pTAS2563->mtimerwork.function = timer_func;
+       //INIT_WORK(&pTAS2563->mtimerwork, timer_func);
+
+       request_firmware_nowait(THIS_MODULE, 1, pFWName,
+               pTAS2563->dev, GFP_KERNEL, pTAS2563, tas2563_fw_ready);
+
+err:
+
+       return nResult;
+}
+
+static int tas2563_i2c_remove(struct i2c_client *pClient)
+{
+       struct tas2563_priv *pTAS2563 = i2c_get_clientdata(pClient);
+
+       dev_info(pTAS2563->dev, "%s\n", __func__);
+
+#ifdef CONFIG_TAS2563_CODEC
+       tas2563_deregister_codec(pTAS2563);
+       mutex_destroy(&pTAS2563->codec_lock);
+#endif
+
+#ifdef CONFIG_TAS2563_MISC
+       tas2563_deregister_misc(pTAS2563);
+       mutex_destroy(&pTAS2563->file_lock);
+#endif
+
+       mutex_destroy(&pTAS2563->dev_lock);
+       return 0;
+}
+
+static const struct i2c_device_id tas2563_i2c_id[] = {
+       {"tas2563", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tas2563_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tas2563_of_match[] = {
+       {.compatible = "ti,tas2563"},
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, tas2563_of_match);
+#endif
+
+static struct i2c_driver tas2563_i2c_driver = {
+       .driver = {
+                       .name = "tas2563",
+                       .owner = THIS_MODULE,
+#if defined(CONFIG_OF)
+                       .of_match_table = of_match_ptr(tas2563_of_match),
+#endif
+               },
+       .probe = tas2563_i2c_probe,
+       .remove = tas2563_i2c_remove,
+       .id_table = tas2563_i2c_id,
+};
+
+module_i2c_driver(tas2563_i2c_driver);
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TAS2563 I2C Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
+
+#endif
diff --git a/tas2563.h b/tas2563.h
new file mode 100644 (file)
index 0000000..ecc5ed7
--- /dev/null
+++ b/tas2563.h
@@ -0,0 +1,853 @@
+
+#ifndef __TAS2563_
+#define __TAS2563_
+
+#define TAS2563_YRAM_BOOK1                             140
+
+#define TAS2563_YRAM1_PAGE                             42
+#define TAS2563_YRAM1_START_REG                        88
+#define TAS2563_YRAM1_END_REG                  127
+
+#define TAS2563_YRAM2_START_PAGE               43
+#define TAS2563_YRAM2_END_PAGE                 49
+#define TAS2563_YRAM2_START_REG                        8
+#define TAS2563_YRAM2_END_REG                  127
+
+#define TAS2563_YRAM3_PAGE                             50
+#define TAS2563_YRAM3_START_REG                        8
+#define TAS2563_YRAM3_END_REG                  27
+
+/* should not include B0_P53_R44-R47 */
+#define TAS2563_YRAM_BOOK2                             0
+#define TAS2563_YRAM4_START_PAGE               50
+#define TAS2563_YRAM4_END_PAGE                 60
+#define TAS2563_YRAM4_START_REG                        8
+#define TAS2563_YRAM4_END_REG                  127
+
+#define TAS2563_YRAM5_PAGE                             61
+#define TAS2563_YRAM5_START_REG                        8
+#define TAS2563_YRAM5_END_REG                  27
+
+struct TYCRC {
+       unsigned char mnOffset;
+       unsigned char mnLen;
+};
+
+
+/* Book Control Register (available in page0 of each book) */
+#define TAS2563_BOOKCTL_PAGE            0
+#define TAS2563_BOOKCTL_REG         127
+
+#define TAS2563_REG(book, page, reg)           ((((unsigned int)book * 256 * 128) + \
+                                                ((unsigned int)page * 128)) + reg)
+
+    /* Page */
+#define TAS2563_Page  TAS2563_REG(0x0, 0x0, 0x00)
+#define TAS2563_Page_Page_Mask  (0xff << 0)
+
+
+#define TAS2563_BOOK_ID(reg)                   (reg / (256 * 128))
+
+#define TAS2563_PAGE_ID(reg)                   ((reg % (256 * 128)) / 128)
+
+#define TAS2563_BOOK_REG(reg)                  (reg % (256 * 128))
+
+#define TAS2563_PAGE_REG(reg)                  ((reg % (256 * 128)) % 128)
+
+
+    /* Software Reset */
+#define TAS2563_SoftwareReset  TAS2563_REG(0x0, 0X0, 0x01)
+#define TAS2563_SoftwareReset_SoftwareReset_Mask  (0x1 << 0),
+#define TAS2563_SoftwareReset_SoftwareReset_DontReset  (0x0 << 0)
+#define TAS2563_SoftwareReset_SoftwareReset_Reset  (0x1 << 0)
+
+    /* Power Control */
+#define TAS2563_PowerControl  TAS2563_REG(0x0, 0x0, 0x02)
+#define TAS2563_PowerControl_ISNSPower_Mask  (0x1 << 3)
+#define TAS2563_PowerControl_ISNSPower_Active  (0x0 << 3)
+#define TAS2563_PowerControl_ISNSPower_PoweredDown  (0x1 << 3)
+#define TAS2563_PowerControl_VSNSPower_Mask  (0x1 << 2)
+#define TAS2563_PowerControl_VSNSPower_Active  (0x0 << 2)
+#define TAS2563_PowerControl_VSNSPower_PoweredDown  (0x1 << 2)
+#define TAS2563_PowerControl_OperationalMode10_Mask  (0x3 << 0)
+#define TAS2563_PowerControl_OperationalMode10_Active  (0x0 << 0)
+#define TAS2563_PowerControl_OperationalMode10_Mute  (0x1 << 0)
+#define TAS2563_PowerControl_OperationalMode10_Shutdown  (0x2 << 0)
+
+       /* data format */
+#define TAS2563_DATAFORMAT_SHIFT               2
+#define TAS2563_DATAFORMAT_I2S                 0x0
+#define TAS2563_DATAFORMAT_DSP                 0x1
+#define TAS2563_DATAFORMAT_RIGHT_J             0x2
+#define TAS2563_DATAFORMAT_LEFT_J              0x3
+
+#define TAS2563_DAI_FMT_MASK           (0x7 << TAS2563_DATAFORMAT_SHIFT)
+
+    /* Playback Configuration Reg0 */
+#define TAS2563_PlaybackConfigurationReg0  TAS2563_REG(0x0, 0x0, 0x03)
+#define TAS2563_PlaybackConfigurationReg0_PDMPinMapping_Mask  (0x1 << 7)
+#define TAS2563_PlaybackConfigurationReg0_PDMPinMapping_Pdm0  (0x0 << 7)
+#define TAS2563_PlaybackConfigurationReg0_PDMPinMapping_Pdm1  (0x1 << 7)
+#define TAS2563_PlaybackConfigurationReg0_PlaybackPDMSource_Mask  (0x1 << 6)
+#define TAS2563_PlaybackConfigurationReg0_PlaybackSource_Mask  (0x1 << 5)
+#define TAS2563_PlaybackConfigurationReg0_PlaybackSource_Pcm  (0x0 << 5)
+#define TAS2563_PlaybackConfigurationReg0_PlaybackSource_Pdm  (0x1 << 5)
+#define TAS2563_PlaybackConfigurationReg0_AmplifierLevel40_Mask  (0x1f << 0)
+
+    /* Misc Configuration Reg0 */
+#define TAS2563_MiscConfigurationReg0  TAS2563_REG(0x0, 0x0, 0x04)
+#define TAS2563_MiscConfigurationReg0_DVCRampRate76_Mask  (0x3 << 6)
+#define TAS2563_MiscConfigurationReg0_DVCRampRate76_0_5dbPer1Sample  (0x0 << 6)
+#define TAS2563_MiscConfigurationReg0_DVCRampRate76_0_5dbPer4Sample  (0x1 << 6)
+#define TAS2563_MiscConfigurationReg0_DVCRampRate76_0_5dbPer8Sample  (0x2 << 6)
+#define TAS2563_MiscConfigurationReg0_DVCRampRate76_VolRampDisabled  (0x3 << 6)
+#define TAS2563_MiscConfigurationReg0_OCERetry_Mask  (0x1 << 5)
+#define TAS2563_MiscConfigurationReg0_OCERetry_DoNotRetry  (0x0 << 5)
+#define TAS2563_MiscConfigurationReg0_OCERetry_Retry  (0x1 << 5)
+#define TAS2563_MiscConfigurationReg0_OTERetry_Mask  (0x1 << 4)
+#define TAS2563_MiscConfigurationReg0_OTERetry_DoNotRetry  (0x0 << 4)
+#define TAS2563_MiscConfigurationReg0_OTERetry_Retry  (0x1 << 4)
+#define TAS2563_MiscConfigurationReg0_IRQZPull_Mask  (0x1 << 3)
+#define TAS2563_MiscConfigurationReg0_IRQZPull_Disabled  (0x0 << 3)
+#define TAS2563_MiscConfigurationReg0_IRQZPull_Enabled  (0x1 << 3)
+#define TAS2563_MiscConfigurationReg0_AMPSS_Mask  (0x1 << 2)
+#define TAS2563_MiscConfigurationReg0_AMPSS_Disabled  (0x0 << 2)
+#define TAS2563_MiscConfigurationReg0_AMPSS_Enabled  (0x1 << 2)
+
+
+    /* TDM Configuration Reg0 */
+#define TAS2563_TDMConfigurationReg0  TAS2563_REG(0x0, 0x0, 0x06)
+#define TAS2563_TDMConfigurationReg0_SAMPRATERAMP_Mask  (0x1 << 5)
+#define TAS2563_TDMConfigurationReg0_SAMPRATERAMP_48KHz  (0x0 << 5)
+#define TAS2563_TDMConfigurationReg0_SAMPRATERAMP_44_1KHz  (0x1 << 5)
+#define TAS2563_TDMConfigurationReg0_DETECTSAMPRATE_Mask  (0x1 << 4)
+#define TAS2563_TDMConfigurationReg0_DETECTSAMPRATE_Disabled  (0x1 << 4)
+#define TAS2563_TDMConfigurationReg0_DETECTSAMPRATE_Enabled  (0x0 << 4)
+#define TAS2563_TDMConfigurationReg0_SAMPRATE31_Mask  (0x7 << 1)
+#define TAS2563_TDMConfigurationReg0_SAMPRATE31_44_1_48kHz  (0x4 << 1)
+#define TAS2563_TDMConfigurationReg0_SAMPRATE31_88_2_96kHz  (0x5 << 1)
+#define TAS2563_TDMConfigurationReg0_SAMPRATE31_176_4_192kHz  (0x6 << 1)
+#define TAS2563_TDMConfigurationReg0_FRAMESTART_Mask  (0x1 << 0)
+#define TAS2563_TDMConfigurationReg0_FRAMESTART_LowToHigh  (0x0 << 0)
+#define TAS2563_TDMConfigurationReg0_FRAMESTART_HighToLow  (0x1 << 0)
+
+    /* TDM Configuration Reg1 */
+#define TAS2563_TDMConfigurationReg1  TAS2563_REG(0x0, 0x0, 0x07)
+#define TAS2563_TDMConfigurationReg1_RXJUSTIFY_Mask  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg1_RXJUSTIFY_Left  (0x0 << 6)
+#define TAS2563_TDMConfigurationReg1_RXJUSTIFY_Right  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg1_RXOFFSET51_Mask  (0x1f << 1)
+#define TAS2563_TDMConfigurationReg1_RXOFFSET51_Shift (1)
+#define TAS2563_TDMConfigurationReg1_RXEDGE_Mask  (0x1 << 0)
+#define TAS2563_TDMConfigurationReg1_RXEDGE_Rising  (0x0 << 0)
+#define TAS2563_TDMConfigurationReg1_RXEDGE_Falling  (0x1 << 0)
+
+    /* TDM Configuration Reg2 */
+#define TAS2563_TDMConfigurationReg2  TAS2563_REG(0x0, 0x0, 0x08)
+#define TAS2563_TDMConfigurationReg2_RXSCFG54_Mask  (0x3 << 4)
+#define TAS2563_TDMConfigurationReg2_RXSCFG54_Mono_I2C  (0x0 << 4)
+#define TAS2563_TDMConfigurationReg2_RXSCFG54_Mono_Left  (0x1 << 4)
+#define TAS2563_TDMConfigurationReg2_RXSCFG54_Mono_Right  (0x2 << 4)
+#define TAS2563_TDMConfigurationReg2_RXSCFG54_Stereo_DownMix  (0x3 << 4)
+#define TAS2563_TDMConfigurationReg2_RXWLEN32_Mask  (0x3 << 2)
+#define TAS2563_TDMConfigurationReg2_RXWLEN32_16Bits  (0x0 << 2)
+#define TAS2563_TDMConfigurationReg2_RXWLEN32_20Bits  (0x1 << 2)
+#define TAS2563_TDMConfigurationReg2_RXWLEN32_24Bits  (0x2 << 2)
+#define TAS2563_TDMConfigurationReg2_RXWLEN32_32Bits  (0x3 << 2)
+#define TAS2563_TDMConfigurationReg2_RXSLEN10_Mask  (0x3 << 0)
+#define TAS2563_TDMConfigurationReg2_RXSLEN10_16Bits  (0x0 << 0)
+#define TAS2563_TDMConfigurationReg2_RXSLEN10_24Bits  (0x1 << 0)
+#define TAS2563_TDMConfigurationReg2_RXSLEN10_32Bits  (0x2 << 0)
+
+    /* TDM Configuration Reg3 */
+#define TAS2563_TDMConfigurationReg3  TAS2563_REG(0x0, 0x0, 0x09)
+#define TAS2563_TDMConfigurationReg3_RXSLOTRight74_Mask  (0xf << 4)
+#define TAS2563_TDMConfigurationReg3_RXSLOTRight74_Shift 4
+#define TAS2563_TDMConfigurationReg3_RXSLOTLeft30_Mask  (0xf << 0)
+#define TAS2563_TDMConfigurationReg3_RXSLOTLeft30_Shift 0
+
+    /* TDM Configuration Reg4 */
+#define TAS2563_TDMConfigurationReg4  TAS2563_REG(0x0, 0x0, 0x0a)
+#define TAS2563_TDMConfigurationReg4_TXKEEPER_Mask  (0x1 << 5)
+#define TAS2563_TDMConfigurationReg4_TXKEEPER_Disable  (0x0 << 5)
+#define TAS2563_TDMConfigurationReg4_TXKEEPER_Enable  (0x1 << 5)
+#define TAS2563_TDMConfigurationReg4_TXFILL_Mask  (0x1 << 4)
+#define TAS2563_TDMConfigurationReg4_TXFILL_Transmit0  (0x0 << 4)
+#define TAS2563_TDMConfigurationReg4_TXFILL_TransmitHiz  (0x1 << 4)
+#define TAS2563_TDMConfigurationReg4_TXOFFSET31_Mask  (0x7 << 1)
+#define TAS2563_TDMConfigurationReg4_TXEDGE_Mask  (0x1 << 0)
+#define TAS2563_TDMConfigurationReg4_TXEDGE_Rising  (0x0 << 0)
+#define TAS2563_TDMConfigurationReg4_TXEDGE_Falling  (0x1 << 0)
+
+    /* TDM Configuration Reg5 */
+#define TAS2563_TDMConfigurationReg5  TAS2563_REG(0x0, 0x0, 0x0b)
+#define TAS2563_TDMConfigurationReg5_VSNSTX_Mask  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg5_VSNSTX_Disable  (0x0 << 6)
+#define TAS2563_TDMConfigurationReg5_VSNSTX_Enable  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg5_VSNSSLOT50_Mask  (0x3f << 0)
+
+    /* TDM Configuration Reg6 */
+#define TAS2563_TDMConfigurationReg6  TAS2563_REG(0x0, 0x0, 0xc)
+#define TAS2563_TDMConfigurationReg6_ISNSTX_Mask  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg6_ISNSTX_Disable  (0x0 << 6)
+#define TAS2563_TDMConfigurationReg6_ISNSTX_Enable  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg6_ISNSSLOT50_Mask  (0x3f << 0)
+
+    /* TDM Configuration Reg7 */
+#define TAS2563_TDMConfigurationReg7  TAS2563_REG(0x0, 0x0, 0x0d)
+#define TAS2563_TDMConfigurationReg7_PDMTX_Mask  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg7_PDMTX_Disable  (0x0 << 6)
+#define TAS2563_TDMConfigurationReg7_PDMTX_Enable  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg7_PDMSLOT50_Mask  (0x3f << 0)
+
+    /* TDM Configuration Reg8 */
+#define TAS2563_TDMConfigurationReg8  TAS2563_REG(0x0, 0x0, 0x0e)
+#define TAS2563_TDMConfigurationReg8_VBATSLEN_Mask  (0x1 << 7)
+#define TAS2563_TDMConfigurationReg8_VBATSLEN_8Bits  (0x0 << 7)
+#define TAS2563_TDMConfigurationReg8_VBATSLEN_16Bits  (0x1 << 7)
+#define TAS2563_TDMConfigurationReg8_VBATTX_Mask  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg8_VBATTX_Disable  (0x0 << 6)
+#define TAS2563_TDMConfigurationReg8_VBATTX_Enable  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg8_VBATSLOT50_Mask  (0x3f << 0)
+
+    /* TDM Configuration Reg9 */
+#define TAS2563_TDMConfigurationReg9  TAS2563_REG(0x0, 0x0, 0xf)
+#define TAS2563_TDMConfigurationReg9_TEMPTX_Mask  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg9_TEMPTX_Disable  (0x0 << 6)
+#define TAS2563_TDMConfigurationReg9_TEMPTX_Enable  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg9_TEMPSLOT50_Mask  (0x3f << 0)
+
+    /* TDM Configuration Reg10 */
+#define TAS2563_TDMConfigurationReg10  TAS2563_REG(0x0, 0x0, 0x10)
+#define TAS2563_TDMConfigurationReg10_GAINTX_Mask  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg10_GAINTX_Disable  (0x0 << 6)
+#define TAS2563_TDMConfigurationReg10_GAINTX_Enable  (0x1 << 6)
+#define TAS2563_TDMConfigurationReg10_GAINSLOT50_Mask  (0x3f << 0)
+
+    /* Limiter Configuration Reg0 */
+#define TAS2563_LimiterConfigurationReg0  TAS2563_REG(0x0, 0x0, 0x12)
+#define TAS2563_LimiterConfigurationReg0_LIMATKST54_Mask  (0x3 << 4)
+#define TAS2563_LimiterConfigurationReg0_LIMATKST54_1  (0x2 << 4)
+#define TAS2563_LimiterConfigurationReg0_LIMATKST54_2  (0x3 << 4)
+#define TAS2563_LimiterConfigurationReg0_LIMATKST54_0_25  (0x0 << 4)
+#define TAS2563_LimiterConfigurationReg0_LIMATKST54_0_5  (0x1 << 4)
+#define TAS2563_LimiterConfigurationReg0_LIMATKRT31_Mask  (0x7 << 1)
+#define TAS2563_LimiterConfigurationReg0_LIMATKRT31_5  (0x0 << 1)
+#define TAS2563_LimiterConfigurationReg0_LIMATKRT31_10  (0x1 << 1)
+#define TAS2563_LimiterConfigurationReg0_LIMATKRT31_20  (0x2 << 1)
+#define TAS2563_LimiterConfigurationReg0_LIMATKRT31_40  (0x3 << 1)
+#define TAS2563_LimiterConfigurationReg0_LIMATKRT31_80  (0x4 << 1)
+#define TAS2563_LimiterConfigurationReg0_LIMATKRT31_160  (0x5 << 1)
+#define TAS2563_LimiterConfigurationReg0_LIMATKRT31_320  (0x6 << 1)
+#define TAS2563_LimiterConfigurationReg0_LIMATKRT31_640  (0x7 << 1)
+#define TAS2563_LimiterConfigurationReg0_LIMEN_Mask  (0x1 << 0)
+#define TAS2563_LimiterConfigurationReg0_LIMEN_Disabled  (0x0 << 0)
+#define TAS2563_LimiterConfigurationReg0_LIMEN_Enabled  (0x1 << 0)
+
+    /* Limiter Configuration Reg1 */
+#define TAS2563_LimiterConfigurationReg1  TAS2563_REG(0x0, 0x0, 0x13)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSST76_Mask  (0x3 << 6)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSST76_1  (0x2 << 6)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSST76_2  (0x3 << 6)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSST76_0_25  (0x0 << 6)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSST76_0_5  (0x1 << 6)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSRT53_Mask  (0x7 << 3)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSRT53_10  (0x0 << 3)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSRT53_50  (0x1 << 3)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSRT53_100  (0x2 << 3)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSRT53_250  (0x3 << 3)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSRT53_500  (0x4 << 3)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSRT53_750  (0x5 << 3)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSRT53_1000  (0x6 << 3)
+#define TAS2563_LimiterConfigurationReg1_LIMRLSRT53_1500  (0x7 << 3)
+#define TAS2563_LimiterConfigurationReg1_LIMHLDTM20_Mask  (0x7 << 0)
+#define TAS2563_LimiterConfigurationReg1_LIMHLDTM20_0  (0x0 << 0)
+#define TAS2563_LimiterConfigurationReg1_LIMHLDTM20_10  (0x1 << 0)
+#define TAS2563_LimiterConfigurationReg1_LIMHLDTM20_25  (0x2 << 0)
+#define TAS2563_LimiterConfigurationReg1_LIMHLDTM20_50  (0x3 << 0)
+#define TAS2563_LimiterConfigurationReg1_LIMHLDTM20_100  (0x4 << 0)
+#define TAS2563_LimiterConfigurationReg1_LIMHLDTM20_250  (0x5 << 0)
+#define TAS2563_LimiterConfigurationReg1_LIMHLDTM20_500  (0x6 << 0)
+#define TAS2563_LimiterConfigurationReg1_LIMHLDTM20_1000  (0x7 << 0)
+
+    /* Brown Out Prevention Reg0 */
+#define TAS2563_BrownOutPreventionReg0  TAS2563_REG(0x0, 0x0, 0x1B)
+#define TAS2563_BrownOutPreventionReg0_LIMSLOPE54_Mask  (0x3 << 4)
+#define TAS2563_BrownOutPreventionReg0_LIMSLOPE54_1  (0x0 << 4)
+#define TAS2563_BrownOutPreventionReg0_LIMSLOPE54_2  (0x2 << 4)
+#define TAS2563_BrownOutPreventionReg0_LIMSLOPE54_4  (0x3 << 4)
+#define TAS2563_BrownOutPreventionReg0_LIMSLOPE54_1_5  (0x1 << 4)
+#define TAS2563_BrownOutPreventionReg0_BOPHLDCLR_Mask  (0x1 << 3)
+#define TAS2563_BrownOutPreventionReg0_BOPHLDCLR_DontClear  (0x0 << 3)
+#define TAS2563_BrownOutPreventionReg0_BOPHLDCLR_Clear  (0x1 << 3)
+#define TAS2563_BrownOutPreventionReg0_BOPINFHLD_Mask  (0x1 << 2)
+#define TAS2563_BrownOutPreventionReg0_BOPINFHLD_UseHoldTime  (0x0 << 2)
+#define TAS2563_BrownOutPreventionReg0_BOPINFHLD_HoldUntilCleared  (0x1 << 2)
+#define TAS2563_BrownOutPreventionReg0_BOPMUTE_Mask  (0x1 << 1)
+#define TAS2563_BrownOutPreventionReg0_BOPMUTE_DoNotMute  (0x0 << 1)
+#define TAS2563_BrownOutPreventionReg0_BOPMUTE_Mute  (0x1 << 1)
+#define TAS2563_BrownOutPreventionReg0_BOPEN_Mask  (0x1 << 0)
+#define TAS2563_BrownOutPreventionReg0_BOPEN_Disabled  (0x0 << 0)
+#define TAS2563_BrownOutPreventionReg0_BOPEN_Enabled  (0x1 << 0)
+
+    /* Brown Out Prevention Reg1 */
+#define TAS2563_BrownOutPreventionReg1  TAS2563_REG(0x0, 0x0, 0x1C)
+#define TAS2563_BrownOutPreventionReg1_BOPTH70_Mask  (0xff << 0)
+
+    /* Brown Out Prevention Reg2 */
+#define TAS2563_BrownOutPreventionReg2  TAS2563_REG(0x0, 0x0, 0x1D)
+#define TAS2563_BrownOutPreventionReg2_BOPATKRT75_Mask  (0x7 << 5)
+#define TAS2563_BrownOutPreventionReg2_BOPATKRT75_5  (0x0 << 5)
+#define TAS2563_BrownOutPreventionReg2_BOPATKRT75_10  (0x1 << 5)
+#define TAS2563_BrownOutPreventionReg2_BOPATKRT75_20  (0x2 << 5)
+#define TAS2563_BrownOutPreventionReg2_BOPATKRT75_40  (0x3 << 5)
+#define TAS2563_BrownOutPreventionReg2_BOPATKRT75_80  (0x4 << 5)
+#define TAS2563_BrownOutPreventionReg2_BOPATKRT75_160  (0x5 << 5)
+#define TAS2563_BrownOutPreventionReg2_BOPATKRT75_320  (0x6 << 5)
+#define TAS2563_BrownOutPreventionReg2_BOPATKRT75_640  (0x7 << 5)
+#define TAS2563_BrownOutPreventionReg2_BOPATKST43_Mask  (0x3 << 3)
+#define TAS2563_BrownOutPreventionReg2_BOPATKST43_1  (0x1 << 3)
+#define TAS2563_BrownOutPreventionReg2_BOPATKST43_2  (0x3 << 3)
+#define TAS2563_BrownOutPreventionReg2_BOPATKST43_0_5  (0x0 << 3)
+#define TAS2563_BrownOutPreventionReg2_BOPATKST43_1_5  (0x2 << 3)
+#define TAS2563_BrownOutPreventionReg2_BOPHLDTM20_Mask  (0x7 << 0)
+#define TAS2563_BrownOutPreventionReg2_BOPHLDTM20_0  (0x0 << 0)
+#define TAS2563_BrownOutPreventionReg2_BOPHLDTM20_10  (0x1 << 0)
+#define TAS2563_BrownOutPreventionReg2_BOPHLDTM20_25  (0x2 << 0)
+#define TAS2563_BrownOutPreventionReg2_BOPHLDTM20_50  (0x3 << 0)
+#define TAS2563_BrownOutPreventionReg2_BOPHLDTM20_100  (0x4 << 0)
+#define TAS2563_BrownOutPreventionReg2_BOPHLDTM20_250  (0x5 << 0)
+#define TAS2563_BrownOutPreventionReg2_BOPHLDTM20_500  (0x6 << 0)
+#define TAS2563_BrownOutPreventionReg2_BOPHLDTM20_1000  (0x7 << 0)
+
+    /* ICLA Reg0 */
+#define TAS2563_ICLAReg0  TAS2563_REG(0x0, 0x0, 0x1E)
+#define TAS2563_ICLAReg0_ICLAUSEMAX_Mask  (0x1 << 7)
+#define TAS2563_ICLAReg0_ICLAUSEMAX_Min  (0x0 << 7)
+#define TAS2563_ICLAReg0_ICLAUSEMAX_Max  (0x1 << 7)
+#define TAS2563_ICLAReg0_ICLASLOT61_Mask  (0x3f << 1)
+#define TAS2563_ICLAReg0_ICLAEN_Mask  (0x1 << 0)
+#define TAS2563_ICLAReg0_ICLAEN_Disabled  (0x0 << 0)
+#define TAS2563_ICLAReg0_ICLAEN_Enabled  (0x1 << 0)
+
+    /* ICLA Reg1 */
+#define TAS2563_ICLAReg1  TAS2563_REG(0x0, 0x0, 0x1F)
+#define TAS2563_ICLAReg1_ICLASEN_Mask  (0xff << 0)
+#define TAS2563_ICLAReg1_ICLASLOT_7_Disable  (0x0 << 7)
+#define TAS2563_ICLAReg1_ICLASLOT_7_Enable  (0x1 << 7)
+#define TAS2563_ICLAReg1_ICLASLOT_6_Disable  (0x0 << 6)
+#define TAS2563_ICLAReg1_ICLASLOT_6_Enable  (0x1 << 6)
+#define TAS2563_ICLAReg1_ICLASLOT_5_Disable  (0x0 << 5)
+#define TAS2563_ICLAReg1_ICLASLOT_5_Enable  (0x1 << 5)
+#define TAS2563_ICLAReg1_ICLASLOT_4_Disable  (0x0 << 4)
+#define TAS2563_ICLAReg1_ICLASLOT_4_Enable  (0x1 << 4)
+#define TAS2563_ICLAReg1_ICLASLOT_3_Disable  (0x0 << 3)
+#define TAS2563_ICLAReg1_ICLASLOT_3_Enable  (0x1 << 3)
+#define TAS2563_ICLAReg1_ICLASLOT_2_Disable  (0x0 << 2)
+#define TAS2563_ICLAReg1_ICLASLOT_2_Enable  (0x1 << 2)
+#define TAS2563_ICLAReg1_ICLASLOT_1_Disable  (0x0 << 1)
+#define TAS2563_ICLAReg1_ICLASLOT_1_Enable  (0x1 << 1)
+#define TAS2563_ICLAReg1_ICLASLOT_0_Disable  (0x0 << 0)
+#define TAS2563_ICLAReg1_ICLASLOT_0_Enable  (0x1 << 0)
+
+    /* Interrupt Mask Reg0 */
+#define TAS2563_InterruptMaskReg0  TAS2563_REG(0x0, 0x0, 0x1a)
+#define TAS2563_InterruptMaskReg0_LIMMUTEINTMASK_Mask  (0x1 << 7)
+#define TAS2563_InterruptMaskReg0_LIMMUTEINTMASK_Unmask  (0x0 << 7)
+#define TAS2563_InterruptMaskReg0_LIMMUTEINTMASK_Disable  (0x1 << 7)
+#define TAS2563_InterruptMaskReg0_LIMINFHLDINTMASK_Mask  (0x1 << 6)
+#define TAS2563_InterruptMaskReg0_LIMINFHLDINTMASK_Unmask  (0x0 << 6)
+#define TAS2563_InterruptMaskReg0_LIMINFHLDINTMASK_Disable  (0x1 << 6)
+#define TAS2563_InterruptMaskReg0_LIMMAXATNINTMASK_Mask  (0x1 << 5)
+#define TAS2563_InterruptMaskReg0_LIMMAXATNINTMASK_Unmask  (0x0 << 5)
+#define TAS2563_InterruptMaskReg0_LIMMAXATNINTMASK_Disable  (0x1 << 5)
+#define TAS2563_InterruptMaskReg0_VBATLessthanINFINTMASK_Mask  (0x1 << 4)
+#define TAS2563_InterruptMaskReg0_VBATLessthanINFINTMASK_Unmask  (0x0 << 4)
+#define TAS2563_InterruptMaskReg0_VBATLessthanINFINTMASK_Disable  (0x1 << 4)
+#define TAS2563_InterruptMaskReg0_LIMActiveFlagINTMASK_Mask  (0x1 << 3)
+#define TAS2563_InterruptMaskReg0_LIMActiveFlagINTMASK_Unmask  (0x0 << 3)
+#define TAS2563_InterruptMaskReg0_LIMActiveFlagINTMASK_Disable  (0x1 << 3)
+#define TAS2563_InterruptMaskReg0_TDMClockErrorINTMASK_Mask  (0x1 << 2)
+#define TAS2563_InterruptMaskReg0_TDMClockErrorINTMASK_Unmask  (0x0 << 2)
+#define TAS2563_InterruptMaskReg0_TDMClockErrorINTMASK_Disable  (0x1 << 2)
+#define TAS2563_InterruptMaskReg0_OCEINTMASK_Mask  (0x1 << 1)
+#define TAS2563_InterruptMaskReg0_OCEINTMASK_Unmask  (0x0 << 1)
+#define TAS2563_InterruptMaskReg0_OCEINTMASK_Disable  (0x1 << 1)
+#define TAS2563_InterruptMaskReg0_OTEINTMASK_Mask  (0x1 << 0)
+#define TAS2563_InterruptMaskReg0_OTEINTMASK_Unmask  (0x0 << 0)
+#define TAS2563_InterruptMaskReg0_OTEINTMASK_Disable  (0x1 << 0)
+#define TAS2563_InterruptMaskReg0_Disable 0xff
+
+    /* Interrupt Mask Reg1 */
+#define TAS2563_InterruptMaskReg1  TAS2563_REG(0x0, 0x0, 0x1b)
+#define TAS2563_InterruptMaskReg1_PDMAUDDATAINVALIDINTMASK_Mask  (0x1 << 7)
+#define TAS2563_InterruptMaskReg1_PDMAUDDATAINVALIDINTMASK_Unmask  (0x0 << 7)
+#define TAS2563_InterruptMaskReg1_PDMAUDDATAINVALIDINTMASK_Disable  (0x1 << 7)
+#define TAS2563_InterruptMaskReg1_VBATOVLOINTMASK_Mask  (0x1 << 3)
+#define TAS2563_InterruptMaskReg1_VBATOVLOINTMASK_Unmask  (0x0 << 3)
+#define TAS2563_InterruptMaskReg1_VBATOVLOINTMASK_Disable  (0x1 << 3)
+#define TAS2563_InterruptMaskReg1_VBATUVLOINTMASK_Mask  (0x1 << 2)
+#define TAS2563_InterruptMaskReg1_VBATUVLOINTMASK_Unmask  (0x0 << 2)
+#define TAS2563_InterruptMaskReg1_VBATUVLOINTMASK_Disable  (0x1 << 2)
+#define TAS2563_InterruptMaskReg1_BrownOutFlagINTMASK_Mask  (0x1 << 1)
+#define TAS2563_InterruptMaskReg1_BrownOutFlagINTMASK_Unmask  (0x0 << 1)
+#define TAS2563_InterruptMaskReg1_BrownOutFlagINTMASK_Disable  (0x1 << 1)
+#define TAS2563_InterruptMaskReg1_PDMClockErrorINTMASK_Mask  (0x1 << 0)
+#define TAS2563_InterruptMaskReg1_PDMClockErrorINTMASK_Unmask  (0x0 << 0)
+#define TAS2563_InterruptMaskReg1_PDMClockErrorINTMASK_Disable  (0x1 << 0)
+#define TAS2563_InterruptMaskReg1_Disable 0xff
+
+    /* Live-Interrupt Reg0 */
+#define TAS2563_LiveInterruptReg0  TAS2563_REG(0x0, 0x0, 0x1f)
+#define TAS2563_LiveInterruptReg0_LIMMUTE_Mask  (0x1 << 7)
+#define TAS2563_LiveInterruptReg0_LIMMUTE_NoInterrupt  (0x0 << 7)
+#define TAS2563_LiveInterruptReg0_LIMMUTE_Interrupt  (0x1 << 7)
+#define TAS2563_LiveInterruptReg0_LIMINFHLD_Mask  (0x1 << 6)
+#define TAS2563_LiveInterruptReg0_LIMINFHLD_NoInterrupt  (0x0 << 6)
+#define TAS2563_LiveInterruptReg0_LIMINFHLD_Interrupt  (0x1 << 6)
+#define TAS2563_LiveInterruptReg0_LIMMAXATN_Mask  (0x1 << 5)
+#define TAS2563_LiveInterruptReg0_LIMMAXATN_NoInterrupt  (0x0 << 5)
+#define TAS2563_LiveInterruptReg0_LIMMAXATN_Interrupt  (0x1 << 5)
+#define TAS2563_LiveInterruptReg0_VBATLessthanINF_Mask  (0x1 << 4)
+#define TAS2563_LiveInterruptReg0_VBATLessthanINF_NoInterrupt  (0x0 << 4)
+#define TAS2563_LiveInterruptReg0_VBATLessthanINF_Interrupt  (0x1 << 4)
+#define TAS2563_LiveInterruptReg0_LIMActiveFlag_Mask  (0x1 << 3)
+#define TAS2563_LiveInterruptReg0_LIMActiveFlag_NoInterrupt  (0x0 << 3)
+#define TAS2563_LiveInterruptReg0_LIMActiveFlag_Interrupt  (0x1 << 3)
+#define TAS2563_LiveInterruptReg0_TDMClockError_Mask  (0x1 << 2)
+#define TAS2563_LiveInterruptReg0_TDMClockError_NoInterrupt  (0x0 << 2)
+#define TAS2563_LiveInterruptReg0_TDMClockError_Interrupt  (0x1 << 2)
+#define TAS2563_LiveInterruptReg0_OCEFlag_Mask  (0x1 << 1)
+#define TAS2563_LiveInterruptReg0_OCEFlag_NoInterrupt  (0x0 << 1)
+#define TAS2563_LiveInterruptReg0_OCEFlag_Interrupt  (0x1 << 1)
+#define TAS2563_LiveInterruptReg0_OTEFlag_Mask  (0x1 << 0)
+#define TAS2563_LiveInterruptReg0_OTEFlag_NoInterrupt  (0x0 << 0)
+#define TAS2563_LiveInterruptReg0_OTEFlag_Interrupt  (0x1 << 0)
+
+    /* Live-Interrupt Reg1 */
+#define TAS2563_LiveInterruptReg1  TAS2563_REG(0x0, 0x0, 0x20)
+#define TAS2563_LiveInterruptReg1_PDMAUDDATAINVALID_Mask  (0x1 << 7)
+#define TAS2563_LiveInterruptReg1_PDMAUDDATAINVALID_NoInterrupt  (0x0 << 7)
+#define TAS2563_LiveInterruptReg1_PDMAUDDATAINVALID_Interrupt  (0x1 << 7)
+#define TAS2563_LiveInterruptReg1_VBATOVLO_Mask  (0x1 << 3)
+#define TAS2563_LiveInterruptReg1_VBATOVLO_NoInterrupt  (0x0 << 3)
+#define TAS2563_LiveInterruptReg1_VBATOVLO_Interrupt  (0x1 << 3)
+#define TAS2563_LiveInterruptReg1_VBATUVLO_Mask  (0x1 << 2)
+#define TAS2563_LiveInterruptReg1_VBATUVLO_NoInterrupt  (0x0 << 2)
+#define TAS2563_LiveInterruptReg1_VBATUVLO_Interrupt  (0x1 << 2)
+#define TAS2563_LiveInterruptReg1_BrownOutFlag_Mask  (0x1 << 1)
+#define TAS2563_LiveInterruptReg1_BrownOutFlag_NoInterrupt  (0x0 << 1)
+#define TAS2563_LiveInterruptReg1_BrownOutFlag_Interrupt  (0x1 << 1)
+#define TAS2563_LiveInterruptReg1_PDMClockError_Mask  (0x1 << 0)
+#define TAS2563_LiveInterruptReg1_PDMClockError_NoInterrupt  (0x0 << 0)
+#define TAS2563_LiveInterruptReg1_PDMClockError_Interrupt  (0x1 << 0)
+
+    /* Latched-Interrupt Reg0 */
+#define TAS2563_LatchedInterruptReg0  TAS2563_REG(0x0, 0x0, 0x24)
+#define TAS2563_LatchedInterruptReg0_LIMMUTESticky_Mask  (0x1 << 7)
+#define TAS2563_LatchedInterruptReg0_LIMMUTESticky_NoInterrupt  (0x0 << 7)
+#define TAS2563_LatchedInterruptReg0_LIMMUTESticky_Interrupt  (0x1 << 7)
+#define TAS2563_LatchedInterruptReg0_LIMINFHLDSticky_Mask  (0x1 << 6)
+#define TAS2563_LatchedInterruptReg0_LIMINFHLDSticky_NoInterrupt  (0x0 << 6)
+#define TAS2563_LatchedInterruptReg0_LIMINFHLDSticky_Interrupt  (0x1 << 6)
+#define TAS2563_LatchedInterruptReg0_LIMMAXATNSticky_Mask  (0x1 << 5)
+#define TAS2563_LatchedInterruptReg0_LIMMAXATNSticky_NoInterrupt  (0x0 << 5)
+#define TAS2563_LatchedInterruptReg0_LIMMAXATNSticky_Interrupt  (0x1 << 5)
+#define TAS2563_LatchedInterruptReg0_VBATLessthanINFSticky_Mask  (0x1 << 4)
+#define TAS2563_LatchedInterruptReg0_VBATLessthanINFSticky_NoInterrupt \
+       (0x0 << 4)
+#define TAS2563_LatchedInterruptReg0_VBATLessthanINFSticky_Interrupt  (0x1 << 4)
+#define TAS2563_LatchedInterruptReg0_LIMActiveFlagSticky_Mask  (0x1 << 3)
+#define TAS2563_LatchedInterruptReg0_LIMActiveFlagSticky_NoInterrupt  (0x0 << 3)
+#define TAS2563_LatchedInterruptReg0_LIMActiveFlagSticky_Interrupt  (0x1 << 3)
+#define TAS2563_LatchedInterruptReg0_TDMClockErrorSticky_Mask  (0x1 << 2)
+#define TAS2563_LatchedInterruptReg0_TDMClockErrorSticky_NoInterrupt  (0x0 << 2)
+#define TAS2563_LatchedInterruptReg0_TDMClockErrorSticky_Interrupt  (0x1 << 2)
+#define TAS2563_LatchedInterruptReg0_OCEFlagSticky_Mask  (0x1 << 1)
+#define TAS2563_LatchedInterruptReg0_OCEFlagSticky_NoInterrupt  (0x0 << 1)
+#define TAS2563_LatchedInterruptReg0_OCEFlagSticky_Interrupt  (0x1 << 1)
+#define TAS2563_LatchedInterruptReg0_OTEFlagSticky_Mask  (0x1 << 0)
+#define TAS2563_LatchedInterruptReg0_OTEFlagSticky_NoInterrupt  (0x0 << 0)
+#define TAS2563_LatchedInterruptReg0_OTEFlagSticky_Interrupt  (0x1 << 0)
+
+    /* Latched-Interrupt Reg1 */
+#define TAS2563_LatchedInterruptReg1  TAS2563_REG(0x0, 0x0, 0x25)
+#define TAS2563_LatchedInterruptReg1_PDMAUDDATAINVALIDSticky_Mask  (0x1 << 7)
+#define TAS2563_LatchedInterruptReg1_PDMAUDDATAINVALIDSticky_NoInterrupt \
+       (0x0 << 7)
+#define TAS2563_LatchedInterruptReg1_PDMAUDDATAINVALIDSticky_Interrupt \
+       (0x1 << 7)
+#define TAS2563_LatchedInterruptReg1_VBATOVLOSticky_Mask  (0x1 << 3)
+#define TAS2563_LatchedInterruptReg1_VBATOVLOSticky_NoInterrupt  (0x0 << 3)
+#define TAS2563_LatchedInterruptReg1_VBATOVLOSticky_Interrupt  (0x1 << 3)
+#define TAS2563_LatchedInterruptReg1_VBATUVLOSticky_Mask  (0x1 << 2)
+#define TAS2563_LatchedInterruptReg1_VBATUVLOSticky_NoInterrupt  (0x0 << 2)
+#define TAS2563_LatchedInterruptReg1_VBATUVLOSticky_Interrupt  (0x1 << 2)
+#define TAS2563_LatchedInterruptReg1_BrownOutFlagSticky_Mask  (0x1 << 1)
+#define TAS2563_LatchedInterruptReg1_BrownOutFlagSticky_NoInterrupt  (0x0 << 1)
+#define TAS2563_LatchedInterruptReg1_BrownOutFlagSticky_Interrupt  (0x1 << 1)
+#define TAS2563_LatchedInterruptReg1_PDMClockErrorSticky_Mask  (0x1 << 0)
+#define TAS2563_LatchedInterruptReg1_PDMClockErrorSticky_NoInterrupt  (0x0 << 0)
+#define TAS2563_LatchedInterruptReg1_PDMClockErrorSticky_Interrupt  (0x1 << 0)
+
+    /* VBAT MSB */
+#define TAS2563_VBATMSB  TAS2563_REG(0x0, 0x0, 0x27)
+#define TAS2563_VBATMSB_VBATMSB70_Mask  (0xff << 0)
+
+    /* VBAT LSB */
+#define TAS2563_VBATLSB  TAS2563_REG(0x0, 0x0, 0x28)
+#define TAS2563_VBATLSB_VBATLSB74_Mask  (0xf << 4)
+
+    /* TEMP MSB */
+#define TAS2563_TEMPMSB  TAS2563_REG(0x0, 0x0, 0x29)
+#define TAS2563_TEMPMSB_TEMPMSB70_Mask  (0xff << 0)
+
+    /* TEMP LSB */
+#define TAS2563_TEMPLSB  TAS2563_REG(0x0, 0x0, 0x2A)
+#define TAS2563_TEMPLSB_TEMPLSB74_Mask  (0xf << 4)
+
+       /* SDZ Config */
+#define TAS2563_SDZCONFIG  TAS2563_REG(0x0, 0x0, 0x2F)
+#define TAS2563_SDZCONFIG_ICLANONZEROMIN_Mask  (0x1 << 4)
+#define TAS2563_SDZCONFIG_ICLANONZEROMIN_Disable  (0x0 << 4)
+#define TAS2563_SDZCONFIG_ICLANONZEROMIN_Enable  (0x1 << 4)
+#define TAS2563_SDZCONFIG_SDZMODECONF_Mask  (0x3 << 2)
+#define TAS2563_SDZCONFIG_SDZMODECONF_ForcedShutdownAfterTimeout  (0x0 << 2)
+#define TAS2563_SDZCONFIG_SDZMODECONF_ForceShutdown  (0x1 << 2)
+#define TAS2563_SDZCONFIG_SDZMODECONF_NormalShutdown  (0x2 << 2)
+#define TAS2563_SDZCONFIG_SDZMODETIMEOUT_Mask  (0x3 << 0)
+#define TAS2563_SDZCONFIG_SDZMODETIMEOUT_2ms  (0x0 << 0)
+#define TAS2563_SDZCONFIG_SDZMODETIMEOUT_4ms  (0x1 << 0)
+#define TAS2563_SDZCONFIG_SDZMODETIMEOUT_6ms  (0x2 << 0)
+#define TAS2563_SDZCONFIG_SDZMODETIMEOUT_23p8ms  (0x3 << 0)
+
+
+    /* Interrupt Configuration */
+#define TAS2563_InterruptConfiguration  TAS2563_REG(0x0, 0x0, 0x30)
+#define TAS2563_InterruptConfiguration_INTTHRUSW_Mask  (0x1 << 2),
+#define TAS2563_InterruptConfiguration_INTTHRUSW_IntOnIRQZ  (0x0 << 2)
+#define TAS2563_InterruptConfiguration_INTTHRUSW_IntFor2ms  (0x1 << 2)
+#define TAS2563_InterruptConfiguration_PININTConfig10_Mask  (0x3 << 0)
+#define TAS2563_InterruptConfiguration_PININTConfig10_AssertOnLiveInterrupts \
+       (0x0 << 0)
+#define \
+TAS2563_InterruptConfiguration_PININTConfig10_AssertOnLatchedInterrupts \
+       (0x1 << 0)
+#define \
+TAS2563_InterruptConfiguration_PININTConfig10_Assert2msOnLiveInterrupts \
+       (0x2 << 0)
+#define \
+TAS2563_InterruptConfiguration_PININTConfig10_Assert2msOnLatchedInterrupts \
+       (0x3 << 0)
+
+    /* Digital Input Pin Pull Down */
+#define TAS2563_DigitalInputPinPullDown  TAS2563_REG(0x0, 0x0, 0x31)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownSDOUT_Mask  (0x1 << 7)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownSDOUT_Disabled  (0x0 << 7)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownSDOUT_Enabled  (0x1 << 7)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownSDIN_Mask  (0x1 << 6)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownSDIN_Disabled  (0x0 << 6)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownSDIN_Enabled  (0x1 << 6)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownFSYNC_Mask  (0x1 << 5)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownFSYNC_Disabled  (0x0 << 5)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownFSYNC_Enabled  (0x1 << 5)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownSBCLK_Mask  (0x1 << 4)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownSBCLK_Disabled  (0x0 << 4)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownSBCLK_Enabled  (0x1 << 4)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownPDMD0_Mask  (0x1 << 3)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownPDMD0_Disabled  (0x0 << 3)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownPDMD0_Enabled  (0x1 << 3)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownPDMD1_Mask  (0x1 << 2)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownPDMD1_Disabled  (0x0 << 2)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownPDMD1_Enabled  (0x1 << 2)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownPDMCK0_Mask  (0x1 << 1)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownPDMCK0_Disabled  (0x0 << 1)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownPDMCK0_Enabled  (0x1 << 1)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownPDMCK1_Mask  (0x1 << 0)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownPDMCK1_Disabled  (0x0 << 0)
+#define TAS2563_DigitalInputPinPullDown_WKPulldownPDMCK1_Enabled  (0x1 << 0)
+
+    /* Misc IRQ */
+#define TAS2563_MiscIRQ  TAS2563_REG(0x0, 0x0, 0x32)
+#define TAS2563_MiscIRQ_IRQZREQD_Mask  (0x1 << 7)
+#define TAS2563_MiscIRQ_IRQZREQD_ActiveHigh  (0x0 << 7)
+#define TAS2563_MiscIRQ_IRQZREQD_ActiveLow  (0x1 << 7)
+#define TAS2563_MiscIRQ_IRQZBITBANG_Mask  (0x1 << 0)
+#define TAS2563_MiscIRQ_IRQZBITBANG_IRQZInputBuf0  (0x0 << 0)
+#define TAS2563_MiscIRQ_IRQZBITBANG_IRQZInputBuf1  (0x1 << 0)
+
+    /* Clock Configuration */
+#define TAS2563_ClockConfiguration  TAS2563_REG(0x0, 0x0, 0x38)
+#define TAS2563_ClockConfiguration_SBCLKtoFS52_Mask  (0xf << 2)
+#define TAS2563_ClockConfiguration_SBCLKtoFS52_16  (0x0 << 2)
+#define TAS2563_ClockConfiguration_SBCLKtoFS52_24  (0x1 << 2)
+#define TAS2563_ClockConfiguration_SBCLKtoFS52_32  (0x2 << 2)
+#define TAS2563_ClockConfiguration_SBCLKtoFS52_48  (0x3 << 2)
+#define TAS2563_ClockConfiguration_SBCLKtoFS52_64  (0x4 << 2)
+#define TAS2563_ClockConfiguration_SBCLKtoFS52_96  (0x5 << 2)
+#define TAS2563_ClockConfiguration_SBCLKtoFS52_128  (0x6 << 2)
+#define TAS2563_ClockConfiguration_SBCLKtoFS52_192  (0x7 << 2)
+#define TAS2563_ClockConfiguration_SBCLKtoFS52_256  (0x8 << 2)
+#define TAS2563_ClockConfiguration_SBCLKtoFS52_384  (0x9 << 2)
+#define TAS2563_ClockConfiguration_SBCLKtoFS52_512  (0xa << 2)
+#define TAS2563_ClockConfiguration_DISCLKRateDetect10_Mask  (0x3 << 0)
+#define TAS2563_ClockConfiguration_DISCLKRateDetect10_Disabled  (0x1 << 0)
+#define TAS2563_ClockConfiguration_DISCLKRateDetect10_Enabled  (0x0 << 0)
+
+
+#define TAS2563_BDIVSelection_BCLKMaster  TAS2563_REG(0x0, 0x0, 0x3D)
+#define TAS2563_BDIVSelection_BCLKMaster_ClockSource10_Mask  (0x3 << 0)
+#define TAS2563_BDIVSelection_BCLKMaster_ClockSource10_NDIV2Output  (0x0 << 0)
+#define TAS2563_BDIVSelection_BCLKMaster_ClockSource10_NDIV1Output  (0x1 << 0)
+#define TAS2563_BDIVSelection_BCLKMaster_ClockSource10_MCLKOutput  (0x2 << 0)
+#define TAS2563_BDIVSelection_BCLKMaster_ClockSource10_PDMCLK1PAD  (0x3 << 0)
+
+#define TAS2563_BDIVSelection_HOLDSARUPDATE  TAS2563_REG(0x0, 0x0, 0x41)
+#define TAS2563_BDIVSelection_HOLDSARUPDATE10_Mask  (0x1 << 0)
+#define TAS2563_BDIVSelection_HOLDSARUPDATE10_Disabled  (0x0 << 0)
+#define TAS2563_BDIVSelection_HOLDSARUPDATE10_Enabled  (0x1 << 0)
+
+
+    /* TDM Clock detection monitor */
+#define TAS2563_TDMClockdetectionmonitor  TAS2563_REG(0x0, 0x0, 0x77)
+#define TAS2563_TDMClockdetectionmonitor_SBCLKtoFSYNC63_Mask  (0xf << 3)
+#define TAS2563_TDMClockdetectionmonitor_SBCLKtoFSYNC63_16  (0x0 << 3)
+#define TAS2563_TDMClockdetectionmonitor_SBCLKtoFSYNC63_24  (0x1 << 3)
+#define TAS2563_TDMClockdetectionmonitor_SBCLKtoFSYNC63_32  (0x2 << 3)
+#define TAS2563_TDMClockdetectionmonitor_SBCLKtoFSYNC63_48  (0x3 << 3)
+#define TAS2563_TDMClockdetectionmonitor_SBCLKtoFSYNC63_64  (0x4 << 3)
+#define TAS2563_TDMClockdetectionmonitor_SBCLKtoFSYNC63_96  (0x5 << 3)
+#define TAS2563_TDMClockdetectionmonitor_SBCLKtoFSYNC63_128  (0x6 << 3)
+#define TAS2563_TDMClockdetectionmonitor_SBCLKtoFSYNC63_192  (0x7 << 3)
+#define TAS2563_TDMClockdetectionmonitor_SBCLKtoFSYNC63_256  (0x8 << 3)
+#define TAS2563_TDMClockdetectionmonitor_SBCLKtoFSYNC63_384  (0x9 << 3)
+#define TAS2563_TDMClockdetectionmonitor_SBCLKtoFSYNC63_512  (0xf << 3)
+#define TAS2563_TDMClockdetectionmonitor_DetectedSampleRate20_Mask  (0x7 << 0),
+#define TAS2563_TDMClockdetectionmonitor_DetectedSampleRate20_8khz  (0x0 << 0)
+#define TAS2563_TDMClockdetectionmonitor_DetectedSampleRate20_16khz  (0x1 << 0)
+#define TAS2563_TDMClockdetectionmonitor_DetectedSampleRate20_32khz  (0x2 << 0)
+#define TAS2563_TDMClockdetectionmonitor_DetectedSampleRate20_48khz  (0x3 << 0)
+#define TAS2563_TDMClockdetectionmonitor_DetectedSampleRate20_96khz  (0x4 << 0)
+#define TAS2563_TDMClockdetectionmonitor_DetectedSampleRate20_192khz  (0x5 << 0)
+
+    /* Revision and PG ID */
+#define TAS2563_RevisionandPGID  TAS2563_REG(0x0, 0x0, 0x7D)
+#define TAS2563_RevisionandPGID_RevisionID74_Mask  (0xf << 4)
+#define TAS2563_RevisionandPGID_PGID30_Mask  (0xf << 0)
+
+    /* I2C Checksum */
+#define TAS2563_I2CChecksum  TAS2563_REG(0x0, 0x0, 0x7E)
+#define TAS2563_I2CChecksum_I2CChecksum70_Mask  (0xff << 0)
+
+#define TAS2563_SA_COEFF_SWAP_REG      TAS2563_REG(0, 0x35, 0x2c)
+#define TAS2563_CALI_T_REG     TAS2563_REG(0x8c, 0x30, 0x20)
+#define TAS2563_CALI_R0_REG            TAS2563_REG(0x8c, 0x2f, 0x40)
+//////////////////////////////////////
+
+       /* ICN set */
+#define TAS2563_ICN_REG TAS2563_REG(0, 2, 0x5c)        
+
+    /* Book */
+#define TAS2563_Book  TAS2563_REG(0x0, 0x0, 0x7F)
+#define TAS2563_Book_Book70_Mask  (0xff << 0)
+
+#define TAS2563_FW_NAME     "tas2563_uCDSP.bin"
+
+#define TAS2563_POWER_ACTIVE 0
+#define TAS2563_POWER_MUTE 1
+#define TAS2563_POWER_SHUTDOWN 2
+
+#define        TAS2563_APP_ROM1MODE    0
+#define        TAS2563_APP_ROM2MODE    1
+#define        TAS2563_APP_TUNINGMODE  2
+#define        TAS2563_APP_ROM1_96KHZ  3
+#define        TAS2563_APP_ROM2_96KHZ  4
+#define        TAS2563_APP_RAMMODE             5
+
+#define TAS2563_RegisterCount  55
+
+#define ERROR_NONE             0x0000000
+#define ERROR_PLL_ABSENT       0x0000000
+#define ERROR_DEVA_I2C_COMM    0x0000000
+#define ERROR_DEVB_I2C_COMM    0x0000000
+#define ERROR_PRAM_CRCCHK      0x0000000
+#define ERROR_YRAM_CRCCHK      0x0000001
+#define ERROR_OVER_CURRENT     0x0000002
+#define ERROR_DIE_OVERTEMP     0x0000004
+#define ERROR_OVER_VOLTAGE     0x0000008
+#define ERROR_UNDER_VOLTAGE    0x0000010
+#define ERROR_BROWNOUT         0x0000020
+#define ERROR_CLASSD_PWR       0x0000040
+#define ERROR_FAILSAFE      0x4000000
+
+#define TAS2563_COEFFICIENT_TMAX       0x7fffffff
+#define TAS2563_SAFE_GUARD_PATTERN             0x5a
+#define LOW_TEMPERATURE_CHECK_PERIOD 5000      /* 5 second */
+
+struct tas2563_register {
+int book;
+int page;
+int reg;
+};
+
+struct tas2563_dai_cfg {
+unsigned int dai_fmt;
+unsigned int tdm_delay;
+};
+
+struct TBlock {
+       unsigned int mnType;
+       unsigned char mbPChkSumPresent;
+       unsigned char mnPChkSum;
+       unsigned char mbYChkSumPresent;
+       unsigned char mnYChkSum;
+       unsigned int mnCommands;
+       unsigned char *mpData;
+};
+
+struct TData {
+       char mpName[64];
+       char *mpDescription;
+       unsigned int mnBlocks;
+       struct TBlock *mpBlocks;
+};
+
+struct TProgram {
+       char mpName[64];
+       char *mpDescription;
+       unsigned char mnAppMode;
+//     unsigned short mnBoost;
+       unsigned char mnI2sMode;
+       unsigned char mnISnsPD;
+       unsigned char mnVSnsPD;
+       unsigned char mnPowerLDG;
+       struct TData mData;
+};
+
+struct TPLL {
+       char mpName[64];
+       char *mpDescription;
+       struct TBlock mBlock;
+};
+
+
+struct TConfiguration {
+       char mpName[64];
+       char *mpDescription;
+       unsigned int mnDevices;
+       unsigned int mnProgram;
+//     unsigned int mnPLL;
+       unsigned int mnSamplingRate;
+       unsigned char mnPLLSrc;
+       unsigned int mnPLLSrcRate;
+       unsigned int mnFsRate;
+       struct TData mData;
+};
+
+struct TCalibration {
+       char mpName[64];
+       char *mpDescription;
+       unsigned int mnProgram;
+       unsigned int mnConfiguration;
+       struct TData mData;
+};
+
+struct TFirmware {
+       unsigned int mnFWSize;
+       unsigned int mnChecksum;
+       unsigned int mnPPCVersion;
+       unsigned int mnFWVersion;
+       unsigned int mnDriverVersion;
+       unsigned int mnTimeStamp;
+       char mpDDCName[64];
+       char *mpDescription;
+       unsigned int mnDeviceFamily;
+       unsigned int mnDevice;
+       unsigned int mnPLLs;
+       struct TPLL *mpPLLs;
+       unsigned int mnPrograms;
+       struct TProgram *mpPrograms;
+       unsigned int mnConfigurations;
+       struct TConfiguration *mpConfigurations;
+       unsigned int mnCalibrations;
+       struct TCalibration *mpCalibrations;
+};
+
+struct tas2563_priv {
+       struct device *dev;
+       struct regmap *regmap;
+       struct snd_soc_codec *codec;
+       struct mutex dev_lock;
+       struct delayed_work irq_work;
+       struct hrtimer mtimerwork;
+       int mnClkin;
+       int mnClkid;
+       int mnPowerState;
+       bool mbPowerUp;
+       int mnCurrentBook;
+       int mnCurrentPage;
+       struct TFirmware *mpFirmware;
+       struct TFirmware *mpCalFirmware;
+       int mnCurrentProgram;
+       int mnCurrentSampleRate;
+       int mnNewConfiguration;
+       int mnCurrentConfiguration;
+       int mnCurrentCalibration;
+       int mnLoad;
+       int mnASIFormat;
+       int mnResetGPIO;
+       int mnIRQGPIO;
+       int mnIRQ;
+       bool mbIRQEnable;
+       int mnSamplingRate;
+       int mnFrameSize;
+       int mnPLL;
+       int mnPPG;
+       int mnPGID;
+       int mnLeftSlot;
+       int mnRightSlot;
+       int mnSlot_width;
+       int mnVmon_slot_no;
+       int mnImon_slot_no;
+       int mnRestart;
+       int mnCh_size;
+       int mnDieTvReadCounter;
+       int mnPCMFormat;
+       bool mbLoadConfigurationPrePowerUp;
+       bool mbLoadCalibrationPostPowerUp;
+       bool mbCalibrationLoaded;
+       bool mbBypassTMax;
+       int (*read)(struct tas2563_priv *pTAS2563,
+               unsigned int reg, unsigned int *pValue);
+       int (*write)(struct tas2563_priv *pTAS2563,
+               unsigned int reg, unsigned int Value);
+       int (*bulk_read)(struct tas2563_priv *pTAS2563,
+               unsigned int reg, unsigned char *pData, unsigned int len);
+       int (*bulk_write)(struct tas2563_priv *pTAS2563,
+               unsigned int reg, unsigned char *pData, unsigned int len);
+       int (*update_bits)(struct tas2563_priv *pTAS2563,
+               unsigned int reg, unsigned int mask, unsigned int value);
+       void (*hw_reset)(struct tas2563_priv *pTAS2563);
+       void (*clearIRQ)(struct tas2563_priv *pTAS2563);
+       void (*enableIRQ)(struct tas2563_priv *pTAS2563, bool enable);
+           /* device is working, but system is suspended */
+       int (*runtime_suspend)(struct tas2563_priv *pTAS2563);
+       int (*runtime_resume)(struct tas2563_priv *pTAS2563);
+       int (*set_calibration)(struct tas2563_priv *pTAS2563,
+               int calibration);
+       int (*set_config)(struct tas2563_priv *pTAS2563, int config);
+       bool mbRuntimeSuspend;
+
+       unsigned int mnErrCode;
+#ifdef CONFIG_TAS2563_CODEC
+       struct mutex codec_lock;
+#endif
+
+#ifdef CONFIG_TAS2563_MISC
+       int mnDBGCmd;
+       int mnCurrentReg;
+       struct mutex file_lock;
+#endif
+};
+
+#endif /* __TAS2563_ */
diff --git a/tiload.c b/tiload.c
new file mode 100755 (executable)
index 0000000..203e481
--- /dev/null
+++ b/tiload.c
@@ -0,0 +1,412 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** This program is free software; you can redistribute it and/or modify it under
+** the terms of the GNU General Public License as published by the Free Software
+** Foundation; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** File:
+**     tiload.c
+**
+** Description:
+**     utility for TAS2563 Android in-system tuning
+**
+** =============================================================================
+*/
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+
+#include "tiload.h"
+
+/* enable debug prints in the driver */
+//#define DEBUG
+
+static struct cdev *tiload_cdev;
+static int tiload_major; /* Dynamic allocation of Mjr No. */
+static int tiload_opened; /* Dynamic allocation of Mjr No. */
+static struct tas2563_priv *g_TAS2563;
+struct class *tiload_class;
+static unsigned int magic_num;
+
+static char gPage;
+static char gBook;
+/******************************** Debug section *****************************/
+
+
+/*----------------------------------------------------------------------------
+ * Function : tiload_open
+ *
+ * Purpose  : open method for tiload programming interface
+ *----------------------------------------------------------------------------
+ */
+static int tiload_open(struct inode *in, struct file *filp)
+{
+       struct tas2563_priv *pTAS2563 = g_TAS2563;
+
+       dev_info(pTAS2563->dev, "%s\n", __func__);
+
+       if (tiload_opened) {
+               dev_info(pTAS2563->dev, "%s device is already opened\n", "tiload");
+               return -EINVAL;
+       }
+       filp->private_data = (void *)pTAS2563;
+       tiload_opened++;
+       return 0;
+}
+
+/*----------------------------------------------------------------------------
+ * Function : tiload_release
+ *
+ * Purpose  : close method for tiload programming interface
+ *----------------------------------------------------------------------------
+ */
+static int tiload_release(struct inode *in, struct file *filp)
+{
+       struct tas2563_priv *pTAS2563 = (struct tas2563_priv *)filp->private_data;
+
+       dev_info(pTAS2563->dev, "%s\n", __func__);
+       filp->private_data = NULL;
+       tiload_opened--;
+       return 0;
+}
+
+#define MAX_LENGTH 128
+/*----------------------------------------------------------------------------
+ * Function : tiload_read
+ *
+ * Purpose  : read from codec
+ *----------------------------------------------------------------------------
+ */
+static ssize_t tiload_read(struct file *filp, char __user *buf,
+       size_t count, loff_t *offset)
+{
+       struct tas2563_priv *pTAS2563 = (struct tas2563_priv *)filp->private_data;
+       static char rd_data[MAX_LENGTH + 1];
+       unsigned int nCompositeRegister = 0, Value = 0;
+       char reg_addr;
+       size_t size;
+       int ret = 0;
+#ifdef DEBUG
+       /* int i; */
+#endif
+
+       dev_info(pTAS2563->dev, "%s\n", __func__);
+       if (count > MAX_LENGTH) {
+               dev_err(pTAS2563->dev, "Max %d bytes can be read\n", MAX_LENGTH);
+               return -EINVAL;
+       }
+
+       /* copy register address from user space  */
+       size = copy_from_user(&reg_addr, buf, 1);
+       if (size != 0) {
+               dev_err(pTAS2563->dev, "read: copy_from_user failure\n");
+               return -EINVAL;
+       }
+
+       size = count;
+
+       nCompositeRegister = BPR_REG(gBook, gPage, reg_addr);
+       dev_dbg(pTAS2563->dev, " nCompositeRegister:0x%x  gBook:0x%x gpage:0x%x \n", nCompositeRegister, gBook, gPage);
+       if (count == 1) {
+               ret =
+                       pTAS2563->read(pTAS2563, 0x80000000 | nCompositeRegister, &Value);
+               if (ret >= 0)
+                       rd_data[0] = (char) Value;
+       } else if (count > 1) {
+               ret =
+                       pTAS2563->bulk_read(pTAS2563, 0x80000000 | nCompositeRegister,
+                       rd_data, size);
+       }
+       if (ret < 0)
+               dev_err(pTAS2563->dev, "%s, %d, ret=%d, count=%zu error happen!\n",
+                       __func__, __LINE__, ret, count);
+
+#ifdef DEBUG
+       dev_info(pTAS2563->dev, "read size = %d, reg_addr= %x , count = %d\n",
+               (int) size, reg_addr, (int) count);
+/*     for (i = 0; i < (int) size; i++) {
+*              dev_dbg(pTAS2563->dev, "rd_data[%d]=%x\n", i, rd_data[i]);
+*      }
+*/
+#endif
+       if (size != count)
+               dev_err(pTAS2563->dev, "read %d registers from the codec\n", (int) size);
+
+       if (copy_to_user(buf, rd_data, size) != 0) {
+               dev_err(pTAS2563->dev, "copy_to_user failed\n");
+               return -EINVAL;
+       }
+
+       return size;
+}
+
+/*
+ *----------------------------------------------------------------------------
+ * Function : tiload_write
+ *
+ * Purpose  : write to codec
+ *----------------------------------------------------------------------------
+ */
+static ssize_t tiload_write(struct file *filp, const char __user *buf,
+       size_t count, loff_t *offset)
+{
+       struct tas2563_priv *pTAS2563 = (struct tas2563_priv *)filp->private_data;
+       static char wr_data[MAX_LENGTH + 1];
+       char *pData = wr_data;
+       size_t size;
+       unsigned int nCompositeRegister = 0;
+       unsigned int nRegister;
+       int ret = 0;
+#ifdef DEBUG
+       /* int i; */
+#endif
+       dev_info(pTAS2563->dev, "%s\n", __func__);
+
+       if (count > MAX_LENGTH) {
+               dev_err(pTAS2563->dev, "Max %d bytes can be read\n", MAX_LENGTH);
+               return -EINVAL;
+       }
+
+       /* copy buffer from user space  */
+       size = copy_from_user(wr_data, buf, count);
+       if (size != 0) {
+               dev_err(pTAS2563->dev, "copy_from_user failure %d\n", (int) size);
+               return -EINVAL;
+       }
+#ifdef DEBUG
+       dev_info(pTAS2563->dev, "write size = %zu\n", count);
+/* for (i = 0; i < (int) count; i++) {
+*              dev_info(pTAS2563->dev, "wr_data[%d]=%x\n", i, wr_data[i]);
+*      }
+*/
+#endif
+       nRegister = wr_data[0];
+       size = count;
+       if ((nRegister == 127) && (gPage == 0)) {
+               gBook = wr_data[1];
+               return size;
+       }
+
+       if (nRegister == 0) {
+               gPage = wr_data[1];
+               pData++;
+               count--;
+       }
+
+       nCompositeRegister = BPR_REG(gBook, gPage, nRegister);
+       if (count == 2) {
+               ret =
+                       pTAS2563->write(pTAS2563, 0x80000000 | nCompositeRegister,
+                       pData[1]);
+       } else if (count > 2) {
+               ret =
+                       pTAS2563->bulk_write(pTAS2563, 0x80000000 | nCompositeRegister,
+                       &pData[1], count - 1);
+       }
+
+       if (ret < 0)
+               dev_err(pTAS2563->dev, "%s, %d, ret=%d, count=%zu, ERROR Happen\n", __func__,
+                       __LINE__, ret, count);
+
+       return size;
+}
+
+static void tiload_route_IO(struct tas2563_priv *pTAS2563, unsigned int bLock)
+{
+       if (bLock)
+               pTAS2563->write(pTAS2563, 0xAFFEAFFE, 0xBABEBABE);
+       else
+               pTAS2563->write(pTAS2563, 0xBABEBABE, 0xAFFEAFFE);
+}
+
+static long tiload_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       struct tas2563_priv *pTAS2563 = (struct tas2563_priv *)filp->private_data;
+       long num = 0;
+       void __user *argp = (void __user *) arg;
+       int val;
+       struct BPR bpr;
+
+       dev_info(pTAS2563->dev, "%s, cmd=0x%x\n", __func__, cmd);
+/*  if (_IOC_TYPE(cmd) != TILOAD_IOC_MAGIC)
+ *      return -ENOTTY;
+ */
+
+       switch (cmd) {
+       case TILOAD_IOMAGICNUM_GET:
+               num = copy_to_user(argp, &magic_num, sizeof(int));
+               break;
+       case TILOAD_IOMAGICNUM_SET:
+               num = copy_from_user(&magic_num, argp, sizeof(int));
+               dev_info(pTAS2563->dev, "TILOAD_IOMAGICNUM_SET\n");
+               tiload_route_IO(pTAS2563, magic_num);
+               break;
+       case TILOAD_BPR_READ:
+               break;
+       case TILOAD_BPR_WRITE:
+               num = copy_from_user(&bpr, argp, sizeof(struct BPR));
+               dev_info(pTAS2563->dev, "TILOAD_BPR_WRITE: 0x%02X, 0x%02X, 0x%02X\n\r", bpr.nBook,
+                       bpr.nPage, bpr.nRegister);
+               break;
+       case TILOAD_IOCTL_SET_CHL:
+               break;
+
+       case TILOAD_IOCTL_SET_CONFIG:
+               num = copy_from_user(&val, argp, sizeof(val));
+               pTAS2563->set_config(pTAS2563, val);
+               break;
+       case TILOAD_IOCTL_SET_CALIBRATION:
+               num = copy_from_user(&val, argp, sizeof(val));
+               pTAS2563->set_calibration(pTAS2563, val);
+               break;
+
+       default:
+               break;
+       }
+       return num;
+}
+
+#ifdef CONFIG_COMPAT
+static long tiload_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       struct tas2563_priv *pTAS2563 = (struct tas2563_priv *)filp->private_data;
+       long nResult = 0;
+
+       switch (cmd) {
+       case TILOAD_COMPAT_IOMAGICNUM_GET:
+               dev_info(pTAS2563->dev, "%s, TILOAD_COMPAT_IOMAGICNUM_GET=0x%x\n",
+                       __func__, cmd);
+               nResult = tiload_ioctl(filp, TILOAD_IOMAGICNUM_GET,
+                       (unsigned long) compat_ptr(arg));
+               break;
+
+       case TILOAD_COMPAT_IOMAGICNUM_SET:
+               dev_info(pTAS2563->dev, "%s, TILOAD_COMPAT_IOMAGICNUM_SET=0x%x\n",
+                       __func__, cmd);
+               nResult = tiload_ioctl(filp, TILOAD_IOMAGICNUM_SET,
+                       (unsigned long) compat_ptr(arg));
+               break;
+
+       case TILOAD_COMPAT_BPR_READ:
+               dev_info(pTAS2563->dev, "%s, TILOAD_COMPAT_BPR_READ=0x%x\n",
+                       __func__, cmd);
+               nResult = tiload_ioctl(filp, TILOAD_BPR_READ,
+                       (unsigned long) compat_ptr(arg));
+               break;
+
+       case TILOAD_COMPAT_BPR_WRITE:
+               dev_info(pTAS2563->dev, "%s, TILOAD_COMPAT_BPR_WRITE=0x%x\n",
+                       __func__, cmd);
+               nResult = tiload_ioctl(filp, TILOAD_BPR_WRITE,
+                       (unsigned long) compat_ptr(arg));
+               break;
+
+       case TILOAD_COMPAT_IOCTL_SET_CHL:
+               dev_info(pTAS2563->dev, "%s, TILOAD_COMPAT_IOCTL_SET_CHL=0x%x\n",
+                       __func__, cmd);
+               nResult = tiload_ioctl(filp, TILOAD_IOCTL_SET_CHL,
+                       (unsigned long) compat_ptr(arg));
+               break;
+
+       case TILOAD_COMPAT_IOCTL_SET_CONFIG:
+               dev_info(pTAS2563->dev, "%s, TILOAD_COMPAT_IOCTL_SET_CONFIG=0x%x\n",
+                       __func__, cmd);
+               nResult = tiload_ioctl(filp, TILOAD_IOCTL_SET_CONFIG,
+                       (unsigned long) compat_ptr(arg));
+               break;
+
+       case TILOAD_COMPAT_IOCTL_SET_CALIBRATION:
+               dev_info(pTAS2563->dev, "%s, TILOAD_COMPAT_IOCTL_SET_CALIBRATION=0x%x\n",
+                       __func__, cmd);
+               nResult = tiload_ioctl(filp, TILOAD_IOCTL_SET_CALIBRATION,
+                       (unsigned long) compat_ptr(arg));
+               break;
+
+       default:
+               dev_err(pTAS2563->dev, "%s, unsupport compat ioctl=0x%x\n",
+                       __func__, cmd);
+               break;
+       }
+
+       return nResult;
+}
+#endif
+
+/*********** File operations structure for tiload *************/
+static const struct file_operations tiload_fops = {
+       .owner = THIS_MODULE,
+       .open = tiload_open,
+       .release = tiload_release,
+       .read = tiload_read,
+       .write = tiload_write,
+       .unlocked_ioctl = tiload_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = tiload_compat_ioctl,
+#endif
+};
+
+/*----------------------------------------------------------------------------
+ * Function : tiload_driver_init
+ *
+ * Purpose  : Register a char driver for dynamic tiload programming
+ *----------------------------------------------------------------------------
+ */
+int tiload_driver_init(struct tas2563_priv *pTAS2563)
+{
+       int result;
+       dev_t dev = MKDEV(tiload_major, 0);
+
+       g_TAS2563 = pTAS2563;
+
+       dev_info(pTAS2563->dev, "%s\n", __func__);
+
+       result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);
+       if (result < 0) {
+               dev_err(pTAS2563->dev, "cannot allocate major number %d\n", tiload_major);
+               return result;
+       }
+       tiload_class = class_create(THIS_MODULE, DEVICE_NAME);
+       tiload_major = MAJOR(dev);
+       dev_info(pTAS2563->dev, "allocated Major Number: %d\n", tiload_major);
+
+       tiload_cdev = cdev_alloc();
+       cdev_init(tiload_cdev, &tiload_fops);
+       tiload_cdev->owner = THIS_MODULE;
+       tiload_cdev->ops = &tiload_fops;
+
+       if (device_create(tiload_class, NULL, dev, NULL, "tiload_node") == NULL)
+               dev_err(pTAS2563->dev, "Device creation failed\n");
+
+       if (cdev_add(tiload_cdev, dev, 1) < 0) {
+               dev_err(pTAS2563->dev, "tiload_driver: cdev_add failed\n");
+               unregister_chrdev_region(dev, 1);
+               tiload_cdev = NULL;
+               return 1;
+       }
+       dev_info(pTAS2563->dev, "Registered TiLoad driver, Major number: %d\n", tiload_major);
+       /* class_device_create(tiload_class, NULL, dev, NULL, DEVICE_NAME, 0); */
+       return 0;
+}
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("Utility for TAS2563 Android in-system tuning");
+MODULE_LICENSE("GPL v2");
diff --git a/tiload.h b/tiload.h
new file mode 100755 (executable)
index 0000000..f5debb9
--- /dev/null
+++ b/tiload.h
@@ -0,0 +1,65 @@
+/*
+** =============================================================================
+** Copyright (c) 2016  Texas Instruments Inc.
+**
+** This program is free software; you can redistribute it and/or modify it under
+** the terms of the GNU General Public License as published by the Free Software
+** Foundation; version 2.
+**
+** This program is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+**
+** File:
+**     tiload.h
+**
+** Description:
+**     header file for tiload.c
+**
+** =============================================================================
+*/
+
+#ifndef _TILOAD_H
+#define _TILOAD_H
+
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+#endif
+
+#include "tas2563.h"
+
+#define BPR_REG(book, page, reg)               (((book * 256 * 128) + \
+                                                (page * 128)) + reg)
+
+/* typedefs required for the included header files */
+struct BPR {
+       unsigned char nBook;
+       unsigned char nPage;
+       unsigned char nRegister;
+};
+
+/* defines */
+#define DEVICE_NAME     "tiload_node"
+
+#define TILOAD_IOC_MAGIC   0xE0
+#define TILOAD_IOMAGICNUM_GET                  _IOR(TILOAD_IOC_MAGIC, 1, int)
+#define TILOAD_IOMAGICNUM_SET                  _IOW(TILOAD_IOC_MAGIC, 2, int)
+#define TILOAD_BPR_READ                                        _IOR(TILOAD_IOC_MAGIC, 3, struct BPR)
+#define TILOAD_BPR_WRITE                               _IOW(TILOAD_IOC_MAGIC, 4, struct BPR)
+#define TILOAD_IOCTL_SET_CHL                   _IOW(TILOAD_IOC_MAGIC, 5, int)
+#define TILOAD_IOCTL_SET_CONFIG                        _IOW(TILOAD_IOC_MAGIC, 6, int)
+#define TILOAD_IOCTL_SET_CALIBRATION   _IOW(TILOAD_IOC_MAGIC, 7, int)
+
+#ifdef CONFIG_COMPAT
+#define TILOAD_COMPAT_IOMAGICNUM_GET           _IOR(TILOAD_IOC_MAGIC, 1, compat_int_t)
+#define TILOAD_COMPAT_IOMAGICNUM_SET           _IOW(TILOAD_IOC_MAGIC, 2, compat_int_t)
+#define TILOAD_COMPAT_BPR_READ                         _IOR(TILOAD_IOC_MAGIC, 3, struct BPR)
+#define TILOAD_COMPAT_BPR_WRITE                                _IOW(TILOAD_IOC_MAGIC, 4, struct BPR)
+#define TILOAD_COMPAT_IOCTL_SET_CHL                    _IOW(TILOAD_IOC_MAGIC, 5, compat_int_t)
+#define TILOAD_COMPAT_IOCTL_SET_CONFIG         _IOW(TILOAD_IOC_MAGIC, 6, compat_int_t)
+#define TILOAD_COMPAT_IOCTL_SET_CALIBRATION    _IOW(TILOAD_IOC_MAGIC, 7, compat_int_t)
+#endif
+
+int tiload_driver_init(struct tas2563_priv *pTAS2563);
+
+#endif