Support bitwidth change in IV data
[tas2770sw-android/tas2770sw-android.git] / tas2770-codec.c
index 2e77cd107088973e2fc7462aa803d1b7818fe6f5..1e4e852969d68c5ef37128e5396c0a8574de207f 100644 (file)
@@ -8,19 +8,19 @@
 **
 ** This program is distributed in the hope that it will be useful, but WITHOUT
 ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-** FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+** FOR A PARTICULAR PURPOSE.See the GNU General Public License for more details.
 **
 ** File:
 **     tas2770-codec.c
 **
 ** Description:
-**     ALSA SoC driver for Texas Instruments TAS2770 High Performance 4W Smart Amplifier
+**     ALSA SoC driver for TAS2770 20-W Digital Input Mono Class-D Audio Amplifier
+**     with Speaker I/V Sense
 **
 ** =============================================================================
 */
 
 #ifdef CONFIG_TAS2770_CODEC
-
 #define DEBUG
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 
 
 #define TAS2770_MDELAY 0xFFFFFFFE
-//#define KCONTROL_CODEC
 
-static unsigned int tas2770_codec_read(struct snd_soc_codec *codec,  unsigned int reg)
+static int tas2770_set_slot(struct snd_soc_codec *codec, int slot_width);
+
+static unsigned int tas2770_codec_read(struct snd_soc_codec *codec,
+               unsigned int reg)
 {
        struct tas2770_priv *pTAS2770 = snd_soc_codec_get_drvdata(codec);
        int nResult = 0;
@@ -62,10 +64,15 @@ static unsigned int tas2770_codec_read(struct snd_soc_codec *codec,  unsigned in
                dev_err(pTAS2770->dev, "%s, ERROR, reg=0x%x, E=%d\n",
                        __func__, reg, nResult);
        else
-               dev_dbg(pTAS2770->dev, "%s, reg: 0x%x, value: 0x%x\n", __func__, reg, value);
+               dev_dbg(pTAS2770->dev, "%s, reg: 0x%x, value: 0x%x\n",
+                               __func__, reg, value);
 
        mutex_unlock(&pTAS2770->dev_lock);
-       return nResult;
+
+       if (nResult >= 0)
+               return value;
+       else
+               return nResult;
 }
 
 
@@ -79,11 +86,12 @@ static int tas2770_codec_write(struct snd_soc_codec *codec, unsigned int reg,
        mutex_lock(&pTAS2770->dev_lock);
 
        nResult = regmap_write(pTAS2770->regmap, reg, value);
-       if(nResult < 0)
+       if (nResult < 0)
                dev_err(pTAS2770->dev, "%s, ERROR, reg=0x%x, E=%d\n",
                        __func__, reg, nResult);
        else
-               dev_dbg(pTAS2770->dev, "%s, reg: 0x%x, 0x%x\n", __func__, reg, value);
+               dev_dbg(pTAS2770->dev, "%s, reg: 0x%x, 0x%x\n",
+                       __func__, reg, value);
 
        mutex_unlock(&pTAS2770->dev_lock);
 
@@ -100,7 +108,6 @@ static int tas2770_codec_suspend(struct snd_soc_codec *codec)
        mutex_lock(&pTAS2770->codec_lock);
 
        dev_dbg(pTAS2770->dev, "%s\n", __func__);
-//     pTAS2770->runtime_suspend(pTAS2770);
 
        mutex_unlock(&pTAS2770->codec_lock);
        return ret;
@@ -114,290 +121,286 @@ static int tas2770_codec_resume(struct snd_soc_codec *codec)
        mutex_lock(&pTAS2770->codec_lock);
 
        dev_dbg(pTAS2770->dev, "%s\n", __func__);
-//     pTAS2770->runtime_resume(pTAS2770);
 
        mutex_unlock(&pTAS2770->codec_lock);
        return ret;
 }
 
-static const struct snd_kcontrol_new tas2770_asi_controls[] = {
-       SOC_DAPM_SINGLE("Left", TAS2770_TDMConfigurationReg2,
-               4, 1, 0),
-       SOC_DAPM_SINGLE("Right", TAS2770_TDMConfigurationReg2,
-               4, 2, 0),
-       SOC_DAPM_SINGLE("LeftRightDiv2", TAS2770_TDMConfigurationReg2,
-               4, 3, 0),
+static const char * const tas2770_ASI1_src[] = {
+       "I2C offset", "Left", "Right", "LeftRightDiv2",
 };
 
+static SOC_ENUM_SINGLE_DECL(
+       tas2770_ASI1_src_enum, TAS2770_TDMConfigurationReg2,
+       4, tas2770_ASI1_src);
 
-static int tas2770_dac_event(struct snd_soc_dapm_widget *w,
-                       struct snd_kcontrol *kcontrol, int event) 
+static const struct snd_kcontrol_new tas2770_asi1_mux =
+       SOC_DAPM_ENUM("ASI1 Source", tas2770_ASI1_src_enum);
+
+static int tas2770_set_power_state(struct tas2770_priv *pTAS2770, int state)
 {
-#if 1
-       struct snd_soc_codec *codec = w->codec;
-//     struct tas2770_priv *pTAS2770 = snd_soc_codec_get_drvdata(codec);
+       struct snd_soc_codec *codec = pTAS2770->codec;
 
-       switch (event) {
-       case SND_SOC_DAPM_POST_PMU:
+       switch (state) {
+       case TAS2770_POWER_ACTIVE:
                snd_soc_update_bits(codec, TAS2770_PowerControl,
                        TAS2770_PowerControl_OperationalMode10_Mask,
                        TAS2770_PowerControl_OperationalMode10_Active);
                break;
-       case SND_SOC_DAPM_PRE_PMD:
+
+       case TAS2770_POWER_MUTE:
+               snd_soc_update_bits(codec, TAS2770_PowerControl,
+                       TAS2770_PowerControl_OperationalMode10_Mask,
+                       TAS2770_PowerControl_OperationalMode10_Mute);
+               break;
+
+       case TAS2770_POWER_SHUTDOWN:
                snd_soc_update_bits(codec, TAS2770_PowerControl,
                        TAS2770_PowerControl_OperationalMode10_Mask,
                        TAS2770_PowerControl_OperationalMode10_Shutdown);
                break;
-       
+
+       default:
+               dev_err(pTAS2770->dev, "wrong power state setting %d\n", state);
+
        }
-#endif
+
+       pTAS2770->mnPowerState = state;
        return 0;
+}
+
+static int tas2770_dac_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct tas2770_priv *pTAS2770 = snd_soc_codec_get_drvdata(codec);
+
+       mutex_lock(&pTAS2770->codec_lock);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               tas2770_set_power_state(pTAS2770, TAS2770_POWER_MUTE);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               tas2770_set_power_state(pTAS2770, TAS2770_POWER_SHUTDOWN);
+               break;
+
+       }
 
+       mutex_unlock(&pTAS2770->codec_lock);
+       return 0;
 }
 
+static const struct snd_kcontrol_new isense_switch =
+       SOC_DAPM_SINGLE("Switch", TAS2770_PowerControl, 3, 1, 1);
+static const struct snd_kcontrol_new vsense_switch =
+       SOC_DAPM_SINGLE("Switch", TAS2770_PowerControl, 2, 1, 1);
+
 static const struct snd_soc_dapm_widget tas2770_dapm_widgets[] = {
        SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
-       SND_SOC_DAPM_MIXER("ASI1 Sel",
-               TAS2770_TDMConfigurationReg2, 4, 0,
-               &tas2770_asi_controls[0],
-               ARRAY_SIZE(tas2770_asi_controls)),
-       SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0,
-               tas2770_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
-       SND_SOC_DAPM_OUTPUT("OUT")
-
+       SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0,
+                               &tas2770_asi1_mux),
+       SND_SOC_DAPM_SWITCH("ISENSE", TAS2770_PowerControl, 3, 1,
+                       &isense_switch),
+       SND_SOC_DAPM_SWITCH("VSENSE", TAS2770_PowerControl, 2, 1,
+                       &vsense_switch),
+       SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2770_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 tas2770_audio_map[] = {
+       {"ASI1 Sel", "I2C offset", "ASI1"},
        {"ASI1 Sel", "Left", "ASI1"},
        {"ASI1 Sel", "Right", "ASI1"},
        {"ASI1 Sel", "LeftRightDiv2", "ASI1"},
        {"DAC", NULL, "ASI1 Sel"},
        {"OUT", NULL, "DAC"},
+       {"ISENSE", "Switch", "IMON"},
+       {"VSENSE", "Switch", "VMON"},
 };
 
 
 static int tas2770_mute(struct snd_soc_dai *dai, int mute)
 {
-#if 1
        struct snd_soc_codec *codec = dai->codec;
        struct tas2770_priv *pTAS2770 = snd_soc_codec_get_drvdata(codec);
+       dev_dbg(pTAS2770->dev, "%s\n", __func__);
 
        mutex_lock(&pTAS2770->codec_lock);
-       if(mute) {
-               snd_soc_update_bits(codec, TAS2770_PowerControl,
-                       TAS2770_PowerControl_OperationalMode10_Mask,
-                       TAS2770_PowerControl_OperationalMode10_Mute); 
+       if (mute) {
+               tas2770_set_power_state(pTAS2770, TAS2770_POWER_MUTE);
        } else {
-               snd_soc_update_bits(codec, TAS2770_PowerControl,
-                       TAS2770_PowerControl_OperationalMode10_Mask,
-                       TAS2770_PowerControl_OperationalMode10_Active); 
+               tas2770_set_power_state(pTAS2770, TAS2770_POWER_ACTIVE);
        }
        mutex_unlock(&pTAS2770->codec_lock);
-#endif
        return 0;
 }
 
-
-
-static int tas2770_hw_params(struct snd_pcm_substream *substream,
-               struct snd_pcm_hw_params *params,
-               struct snd_soc_dai *dai)
+static int tas2770_set_bitwidth(struct tas2770_priv *pTAS2770, int bitwidth)
 {
-       struct snd_soc_codec *codec = dai->codec;
-       struct tas2770_priv *pTAS2770 = snd_soc_codec_get_drvdata(codec);
-       int blr_clk_ratio;
-       int ret = 0;
-
-       dev_dbg(pTAS2770->dev, "%s, format: %d\n", __func__, params_format(params));
-
-       switch(params_format(params)) {
+       switch (bitwidth) {
        case SNDRV_PCM_FORMAT_S16_LE:
-               snd_soc_update_bits(codec,
+               snd_soc_update_bits(pTAS2770->codec,
                        TAS2770_TDMConfigurationReg2,
-                       TAS2770_TDMConfigurationReg2_RXWLEN32_Mask |
-                       TAS2770_TDMConfigurationReg2_RXSLEN10_Mask,
-                       TAS2770_TDMConfigurationReg2_RXWLEN32_16Bits |
-                       TAS2770_TDMConfigurationReg2_RXWLEN32_16Bits );
-                       pTAS2770->ch_size = 16;
+                       TAS2770_TDMConfigurationReg2_RXWLEN32_Mask,
+                       TAS2770_TDMConfigurationReg2_RXWLEN32_16Bits);
+                       /* If machine driver did not call set slot width */
+                       if (pTAS2770->mnSlot_width == 0)
+                               tas2770_set_slot(pTAS2770->codec, 16);
+               pTAS2770->mnVmon_slot_no = pTAS2770->mnImon_slot_no + 2;
                break;
-       case SNDRV_PCM_FMTBIT_S24_LE:
-                       snd_soc_update_bits(codec,
+       case SNDRV_PCM_FORMAT_S24_LE:
+                       snd_soc_update_bits(pTAS2770->codec,
                        TAS2770_TDMConfigurationReg2,
-                       TAS2770_TDMConfigurationReg2_RXWLEN32_Mask |
-                       TAS2770_TDMConfigurationReg2_RXSLEN10_Mask,
-                       TAS2770_TDMConfigurationReg2_RXWLEN32_24Bits  |
-                       TAS2770_TDMConfigurationReg2_RXWLEN32_24Bits );
-                       pTAS2770->ch_size = 24;
+                       TAS2770_TDMConfigurationReg2_RXWLEN32_Mask,
+                       TAS2770_TDMConfigurationReg2_RXWLEN32_24Bits);
+                       if (pTAS2770->mnSlot_width == 0)
+                               tas2770_set_slot(pTAS2770->codec, 32);
+               pTAS2770->mnVmon_slot_no = pTAS2770->mnImon_slot_no + 4;
                break;
-       case SNDRV_PCM_FMTBIT_S32_LE:
-                       snd_soc_update_bits(codec,
+       case SNDRV_PCM_FORMAT_S32_LE:
+                       snd_soc_update_bits(pTAS2770->codec,
                        TAS2770_TDMConfigurationReg2,
-                       TAS2770_TDMConfigurationReg2_RXWLEN32_Mask |
-                       TAS2770_TDMConfigurationReg2_RXSLEN10_Mask,
-                       TAS2770_TDMConfigurationReg2_RXWLEN32_32Bits |
-                       TAS2770_TDMConfigurationReg2_RXWLEN32_32Bits );
-                       pTAS2770->ch_size = 32;
+                       TAS2770_TDMConfigurationReg2_RXWLEN32_Mask,
+                       TAS2770_TDMConfigurationReg2_RXWLEN32_32Bits);
+                       if (pTAS2770->mnSlot_width == 0)
+                               tas2770_set_slot(pTAS2770->codec, 32);
+               pTAS2770->mnVmon_slot_no = pTAS2770->mnImon_slot_no + 4;
                break;
 
+       default:
+               dev_dbg(pTAS2770->dev, "Not supported params format\n");
+               return -EINVAL;
        }
 
-       blr_clk_ratio = params_channels(params) * pTAS2770->ch_size;
-       dev_dbg(pTAS2770->dev, "blr_clk_ratio: %d\n", blr_clk_ratio);
+       pTAS2770->mnCh_size = bitwidth;
+       dev_dbg(pTAS2770->dev, "mnCh_size: %d\n", pTAS2770->mnCh_size);
+
+       snd_soc_update_bits(pTAS2770->codec,
+               TAS2770_TDMConfigurationReg5,
+               TAS2770_TDMConfigurationReg5_VSNSTX_Mask |
+               TAS2770_TDMConfigurationReg5_VSNSSLOT50_Mask,
+               TAS2770_TDMConfigurationReg5_VSNSTX_Enable |
+               pTAS2770->mnVmon_slot_no);
+       snd_soc_update_bits(pTAS2770->codec,
+               TAS2770_TDMConfigurationReg6,
+               TAS2770_TDMConfigurationReg6_ISNSTX_Mask |
+               TAS2770_TDMConfigurationReg6_ISNSSLOT50_Mask,
+               TAS2770_TDMConfigurationReg6_ISNSTX_Enable |
+               pTAS2770->mnImon_slot_no);
 
-       switch(blr_clk_ratio) {
-       case 16: 
-                       snd_soc_update_bits(codec,
-                               TAS2770_ClockConfiguration,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_Mask,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_16);
-                       break;
-       case 24:
-                       snd_soc_update_bits(codec,
-                               TAS2770_ClockConfiguration,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_Mask,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_24);
-                       break;
-       case 32:
-                       snd_soc_update_bits(codec,
-                               TAS2770_ClockConfiguration,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_Mask,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_32);
-                       break;
-       case 48: 
-                       snd_soc_update_bits(codec,
-                               TAS2770_ClockConfiguration,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_Mask,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_48);
-                       break;                  
-       case 64:                        
-                       snd_soc_update_bits(codec,
-                               TAS2770_ClockConfiguration,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_Mask,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_64);
-                       break;
-       case 96:                        
-                       snd_soc_update_bits(codec,
-                               TAS2770_ClockConfiguration,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_Mask,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_96);
-                       break;
-       case 128:                       
-                       snd_soc_update_bits(codec,
-                               TAS2770_ClockConfiguration,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_Mask,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_128);
-                       break;                  
-       case 192:                       
-                       snd_soc_update_bits(codec,
-                               TAS2770_ClockConfiguration,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_Mask,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_192);
-                       break;
-       case 256:                       
-                       snd_soc_update_bits(codec,
-                               TAS2770_ClockConfiguration,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_Mask,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_256);
-                       break;
-       case 384:                       
-                       snd_soc_update_bits(codec,
-                               TAS2770_ClockConfiguration,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_Mask,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_384);
-                       break;                  
-       case 512:                       
-                       snd_soc_update_bits(codec,
-                               TAS2770_ClockConfiguration,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_Mask,
-                               TAS2770_ClockConfiguration_SBCLKtoFS52_512);
-                       break;
-       default:
-               dev_err(pTAS2770->dev, "Invalid BCLK to FSYNC ratio\n");
-               ret = -EINVAL;
-       }
+       return 0;
+}
 
-       dev_dbg(pTAS2770->dev, "%s, sample rate: %d\n", __func__, params_rate(params));
-       switch(params_rate(params))
-       {
-               case 48000:
-                       snd_soc_update_bits(codec,
+static int tas2770_set_samplerate(struct tas2770_priv *pTAS2770, int samplerate)
+{
+       switch (samplerate) {
+       case 48000:
+                       snd_soc_update_bits(pTAS2770->codec,
                                TAS2770_TDMConfigurationReg0,
                                TAS2770_TDMConfigurationReg0_SAMPRATERAMP_Mask,
                                TAS2770_TDMConfigurationReg0_SAMPRATERAMP_48KHz);
-                       snd_soc_update_bits(codec,
+                       snd_soc_update_bits(pTAS2770->codec,
                                TAS2770_TDMConfigurationReg0,
                                TAS2770_TDMConfigurationReg0_SAMPRATE31_Mask,
                                TAS2770_TDMConfigurationReg0_SAMPRATE31_44_1_48kHz);
                        break;
-               case 44100:
-                       snd_soc_update_bits(codec,
+       case 44100:
+                       snd_soc_update_bits(pTAS2770->codec,
                                TAS2770_TDMConfigurationReg0,
                                TAS2770_TDMConfigurationReg0_SAMPRATERAMP_Mask,
                                TAS2770_TDMConfigurationReg0_SAMPRATERAMP_44_1KHz);
-                       snd_soc_update_bits(codec,
+                       snd_soc_update_bits(pTAS2770->codec,
                                TAS2770_TDMConfigurationReg0,
                                TAS2770_TDMConfigurationReg0_SAMPRATE31_Mask,
                                TAS2770_TDMConfigurationReg0_SAMPRATE31_44_1_48kHz);
                        break;
-               case 96000:
-                       snd_soc_update_bits(codec,
+       case 96000:
+                       snd_soc_update_bits(pTAS2770->codec,
                                TAS2770_TDMConfigurationReg0,
                                TAS2770_TDMConfigurationReg0_SAMPRATERAMP_Mask,
                                TAS2770_TDMConfigurationReg0_SAMPRATERAMP_48KHz);
-                       snd_soc_update_bits(codec,
+                       snd_soc_update_bits(pTAS2770->codec,
                                TAS2770_TDMConfigurationReg0,
                                TAS2770_TDMConfigurationReg0_SAMPRATE31_Mask,
                                TAS2770_TDMConfigurationReg0_SAMPRATE31_88_2_96kHz);
                        break;
-               case 88200:
-                       snd_soc_update_bits(codec,
+       case 88200:
+                       snd_soc_update_bits(pTAS2770->codec,
                                TAS2770_TDMConfigurationReg0,
                                TAS2770_TDMConfigurationReg0_SAMPRATERAMP_Mask,
                                TAS2770_TDMConfigurationReg0_SAMPRATERAMP_44_1KHz);
-                       snd_soc_update_bits(codec,
+                       snd_soc_update_bits(pTAS2770->codec,
                                TAS2770_TDMConfigurationReg0,
                                TAS2770_TDMConfigurationReg0_SAMPRATE31_Mask,
                                TAS2770_TDMConfigurationReg0_SAMPRATE31_88_2_96kHz);
                        break;
-               case 19200:
-                       snd_soc_update_bits(codec,
+       case 19200:
+                       snd_soc_update_bits(pTAS2770->codec,
                                TAS2770_TDMConfigurationReg0,
                                TAS2770_TDMConfigurationReg0_SAMPRATERAMP_Mask,
                                TAS2770_TDMConfigurationReg0_SAMPRATERAMP_48KHz);
-                       snd_soc_update_bits(codec,
+                       snd_soc_update_bits(pTAS2770->codec,
                                TAS2770_TDMConfigurationReg0,
                                TAS2770_TDMConfigurationReg0_SAMPRATE31_Mask,
                                TAS2770_TDMConfigurationReg0_SAMPRATE31_176_4_192kHz);
                        break;
-               case 17640:
-                       snd_soc_update_bits(codec,
+       case 17640:
+                       snd_soc_update_bits(pTAS2770->codec,
                                TAS2770_TDMConfigurationReg0,
                                TAS2770_TDMConfigurationReg0_SAMPRATERAMP_Mask,
                                TAS2770_TDMConfigurationReg0_SAMPRATERAMP_44_1KHz);
-                       snd_soc_update_bits(codec,
+                       snd_soc_update_bits(pTAS2770->codec,
                                TAS2770_TDMConfigurationReg0,
                                TAS2770_TDMConfigurationReg0_SAMPRATE31_Mask,
                                TAS2770_TDMConfigurationReg0_SAMPRATE31_176_4_192kHz);
                        break;
-               default:
+       default:
                        dev_dbg(pTAS2770->dev, "%s, unsupported sample rate\n", __func__);
-                       
+
        }
-       return ret;
+
+       pTAS2770->mnSamplingRate = samplerate;
+       return 0;
 }
 
-static int tas2770_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+static int tas2770_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params,
+               struct snd_soc_dai *dai)
 {
-       u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
        struct snd_soc_codec *codec = dai->codec;
        struct tas2770_priv *pTAS2770 = snd_soc_codec_get_drvdata(codec);
        int ret = 0;
 
-       dev_dbg(pTAS2770->dev, "%s, format=0x%x\n", __func__, fmt);
+       dev_dbg(pTAS2770->dev, "%s, format: %d\n", __func__,
+               params_format(params));
+
+       mutex_lock(&pTAS2770->codec_lock);
+
+       ret = tas2770_set_bitwidth(pTAS2770, params_format(params));
+       if(ret < 0)
+               return ret;
+
+       dev_dbg(pTAS2770->dev, "%s, sample rate: %d\n", __func__,
+               params_rate(params));
+
+       ret = tas2770_set_samplerate(pTAS2770, params_rate(params));
+
+       mutex_unlock(&pTAS2770->codec_lock);
+       return ret;
+}
+
+static int tas2770_set_fmt(struct tas2770_priv *pTAS2770, unsigned int fmt)
+{
+       u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
+       int ret = 0;
+       int value = 0;
 
-#if 1
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-       case SND_SOC_DAIFMT_CBS_CFS: 
+       case SND_SOC_DAIFMT_CBS_CFS:
                asi_cfg_1 = 0x00;
                break;
        default:
@@ -419,7 +422,7 @@ static int tas2770_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
                ret = -EINVAL;
        }
 
-       snd_soc_update_bits(codec, TAS2770_TDMConfigurationReg1, 
+       snd_soc_update_bits(pTAS2770->codec, TAS2770_TDMConfigurationReg1,
                TAS2770_TDMConfigurationReg1_RXEDGE_Mask,
                asi_cfg_1);
 
@@ -435,15 +438,102 @@ static int tas2770_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
                tdm_rx_start_slot = 0;
                break;
        default:
-               dev_err(pTAS2770->dev, "DAI Format is not found, fmt=0x%x\n", fmt);
-               ret = -EINVAL;
+       dev_err(pTAS2770->dev, "DAI Format is not found, fmt=0x%x\n", fmt);
+       ret = -EINVAL;
                break;
        }
 
-       snd_soc_update_bits(codec, TAS2770_TDMConfigurationReg1, 
+       snd_soc_update_bits(pTAS2770->codec, TAS2770_TDMConfigurationReg1,
                TAS2770_TDMConfigurationReg1_RXOFFSET51_Mask,
-               (tdm_rx_start_slot << TAS2770_TDMConfigurationReg1_RXOFFSET51_Shift));
-#endif
+       (tdm_rx_start_slot << TAS2770_TDMConfigurationReg1_RXOFFSET51_Shift));
+
+       snd_soc_update_bits(pTAS2770->codec, TAS2770_TDMConfigurationReg3,
+               TAS2770_TDMConfigurationReg3_RXSLOTLeft30_Mask,
+               (pTAS2770->mnLeftSlot << TAS2770_TDMConfigurationReg3_RXSLOTLeft30_Shift));
+       snd_soc_update_bits(pTAS2770->codec, TAS2770_TDMConfigurationReg3,
+               TAS2770_TDMConfigurationReg3_RXSLOTRight74_Mask,
+       (pTAS2770->mnRightSlot << TAS2770_TDMConfigurationReg3_RXSLOTRight74_Shift));
+
+       value = snd_soc_read(pTAS2770->codec, TAS2770_TDMConfigurationReg3);
+       dev_dbg(pTAS2770->dev, "slot value: 0x%x", value);
+
+       pTAS2770->mnASIFormat = fmt;
+
+       return 0;
+}
+
+static int tas2770_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct tas2770_priv *pTAS2770 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       dev_dbg(pTAS2770->dev, "%s, format=0x%x\n", __func__, fmt);
+       mutex_lock(&pTAS2770->codec_lock);
+
+       ret = tas2770_set_fmt(pTAS2770, fmt);
+
+       mutex_unlock(&pTAS2770->codec_lock);
+       return ret;
+}
+
+static int tas2770_set_slot(struct snd_soc_codec *codec, int slot_width)
+{
+       int ret = 0;
+       struct tas2770_priv *pTAS2770 = snd_soc_codec_get_drvdata(codec);
+
+       switch (slot_width) {
+       case 16:
+       ret = snd_soc_update_bits(codec,
+               TAS2770_TDMConfigurationReg2,
+               TAS2770_TDMConfigurationReg2_RXSLEN10_Mask,
+               TAS2770_TDMConfigurationReg2_RXSLEN10_16Bits);
+       break;
+
+       case 24:
+       ret = snd_soc_update_bits(codec,
+               TAS2770_TDMConfigurationReg2,
+               TAS2770_TDMConfigurationReg2_RXSLEN10_Mask,
+               TAS2770_TDMConfigurationReg2_RXSLEN10_24Bits);
+       break;
+
+       case 32:
+       ret = snd_soc_update_bits(codec,
+               TAS2770_TDMConfigurationReg2,
+               TAS2770_TDMConfigurationReg2_RXSLEN10_Mask,
+               TAS2770_TDMConfigurationReg2_RXSLEN10_32Bits);
+       break;
+
+       case 0:
+       /* Do not change slot width */
+       break;
+
+       default:
+               dev_dbg(pTAS2770->dev, "slot width not supported");
+               ret = -EINVAL;
+       }
+
+       if (ret >= 0)
+               pTAS2770->mnSlot_width = slot_width;
+
+       return ret;
+}
+
+static int tas2770_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 tas2770_priv *pTAS2770 = snd_soc_codec_get_drvdata(codec);
+
+       dev_dbg(pTAS2770->dev, "%s, tx_mask:%d, rx_mask:%d, slots:%d, slot_width:%d",
+                       __func__, tx_mask, rx_mask, slots, slot_width);
+
+       mutex_lock(&pTAS2770->codec_lock);
+       ret = tas2770_set_slot(codec, slot_width);
+       mutex_unlock(&pTAS2770->codec_lock);
+
        return ret;
 }
 
@@ -451,15 +541,14 @@ static struct snd_soc_dai_ops tas2770_dai_ops = {
        .digital_mute = tas2770_mute,
        .hw_params  = tas2770_hw_params,
        .set_fmt    = tas2770_set_dai_fmt,
+       .set_tdm_slot = tas2770_set_dai_tdm_slot,
 };
 
 #define TAS2770_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 
-#define TAS2770_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 \
-                                               SNDRV_PCM_RATE_88200 |\
+#define TAS2770_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
                                                SNDRV_PCM_RATE_96000 |\
-                                               SNDRV_PCM_RATE_176400 |\
                                                SNDRV_PCM_RATE_192000\
                                                )
 
@@ -471,15 +560,15 @@ static struct snd_soc_dai_driver tas2770_dai_driver[] = {
                        .stream_name    = "ASI1 Playback",
                        .channels_min   = 2,
                        .channels_max   = 2,
-                       .rates      = SNDRV_PCM_RATE_8000_192000,
+                       .rates      = TAS2770_RATES,
                        .formats    = TAS2770_FORMATS,
                },
                .capture = {
                        .stream_name    = "ASI1 Capture",
                        .channels_min   = 0,
                        .channels_max   = 2,
-                       .rates          = SNDRV_PCM_RATE_8000_192000,
-                       .formats    = TAS2770_FORMATS,  
+                       .rates          = TAS2770_RATES,
+                       .formats    = TAS2770_FORMATS,
                },
                .ops = &tas2770_dai_ops,
                .symmetric_rates = 1,
@@ -491,6 +580,7 @@ static int tas2770_codec_probe(struct snd_soc_codec *codec)
        struct tas2770_priv *pTAS2770 = snd_soc_codec_get_drvdata(codec);
 
        dev_err(pTAS2770->dev, "%s\n", __func__);
+       pTAS2770->codec = codec;
 
        return 0;
 }
@@ -500,20 +590,17 @@ static int tas2770_codec_remove(struct snd_soc_codec *codec)
        return 0;
 }
 
-#if 0
-
-/*
- * DAC digital volumes. From 0 to 31 dB in 1 dB steps
- */
-static DECLARE_TLV_DB_SCALE(dac_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0);
+static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -12750, 50, 0);
 
 static const struct snd_kcontrol_new tas2770_snd_controls[] = {
-       SOC_SINGLE_TLV("DAC Playback Volume", TAS2770_PlaybackConfigurationReg0, 0, 0x1f, 0,
-                       dac_tlv),
-//     SOC_SINGLE_EXT("TAS2770 PowerCtrl", SND_SOC_NOPM, 0, 0x0001, 0,
-//                     tas2770_power_ctrl_get, tas2770_power_ctrl_put),
+       SOC_SINGLE_TLV("Amp Output Level", TAS2770_PlaybackConfigurationReg0,
+               0, 0x14, 0,
+               tas2770_digital_tlv),
+       SOC_SINGLE_TLV("Playback Volume", TAS2770_PlaybackConfigurationReg2,
+               0, TAS2770_PlaybackConfigurationReg2_VOLMAX, 1,
+               tas2770_playback_volume),
 };
-#endif
 
 static struct snd_soc_codec_driver soc_codec_driver_tas2770 = {
        .probe                  = tas2770_codec_probe,
@@ -523,8 +610,8 @@ static struct snd_soc_codec_driver soc_codec_driver_tas2770 = {
        .suspend                = tas2770_codec_suspend,
        .resume                 = tas2770_codec_resume,
        .component_driver = {
-//             .controls               = tas2770_snd_controls,
-//             .num_controls           = ARRAY_SIZE(tas2770_snd_controls),
+               .controls               = tas2770_snd_controls,
+               .num_controls           = ARRAY_SIZE(tas2770_snd_controls),
                .dapm_widgets           = tas2770_dapm_widgets,
                .num_dapm_widgets       = ARRAY_SIZE(tas2770_dapm_widgets),
                .dapm_routes            = tas2770_audio_map,
@@ -551,6 +638,39 @@ int tas2770_deregister_codec(struct tas2770_priv *pTAS2770)
        return 0;
 }
 
+void tas2770_LoadConfig(struct tas2770_priv *pTAS2770)
+{
+       int ret = 0;
+
+       pTAS2770->hw_reset(pTAS2770);
+       snd_soc_write(pTAS2770->codec, TAS2770_SoftwareReset,
+                       TAS2770_SoftwareReset_SoftwareReset_Reset);
+
+       ret = tas2770_set_slot(pTAS2770->codec, pTAS2770->mnSlot_width);
+       if (ret < 0)
+               goto end;
+
+       ret = tas2770_set_fmt(pTAS2770, pTAS2770->mnASIFormat);
+       if (ret < 0)
+               goto end;
+
+       ret = tas2770_set_bitwidth(pTAS2770, pTAS2770->mnCh_size);
+       if (ret < 0)
+               goto end;
+
+       ret = tas2770_set_samplerate(pTAS2770, pTAS2770->mnSamplingRate);
+       if (ret < 0)
+               goto end;
+
+       ret = tas2770_set_power_state(pTAS2770, pTAS2770->mnPowerState);
+
+end:
+/* power up failed, restart later */
+       if (ret < 0)
+               schedule_delayed_work(&pTAS2770->irq_work,
+                               msecs_to_jiffies(1000));
+}
+
 MODULE_AUTHOR("Texas Instruments Inc.");
 MODULE_DESCRIPTION("TAS2770 ALSA SOC Smart Amplifier driver");
 MODULE_LICENSE("GPL v2");