diff mbox series

[v1] ASoc: tas2781: Add Calibration Kcontrols and tas2563 digtial gain for Chromebook

Message ID 20240522112942.994-1-shenghao-ding@ti.com (mailing list archive)
State New, archived
Headers show
Series [v1] ASoc: tas2781: Add Calibration Kcontrols and tas2563 digtial gain for Chromebook | expand

Commit Message

Shenghao Ding May 22, 2024, 11:29 a.m. UTC
Calibrated data will be set to default after loading DSP config params,
which will cause speaker protection work abnormally. Reload calibrated
data after loading DSP config params.

Signed-off-by: Shenghao Ding <shenghao-ding@ti.com>

---
v1:
 - Changed the copyright year to 2024 in the related files.
 - Add CAL_DAT_SZ for calibrated data size.
 - Add TAS2563_DVC_LVL for digtial gain kcontrol.
 - Add registers for TAS2563 and TAS2781 calibration.
 - Add cali_data_restore for regsiter restore after calibration.
 - Add is_user_space_calidata to store the flag where the calibrated
   from, user space or bin file from driver parsing.
 - Add TASDEVICE_RCA_FW_OK to support only register setting bin in the
   device.
 - Add tas2563_dvc_table for relationship between tas2563 digtial gains
   and register values.
 - Correct the filename in the header comments of tas2781-comlib.c,
   tas2781-lib.c --> tas2781-comlib.c.
 - tasdevice_chn_switch for chip switch among multiple chips(tas2563
   or tas2781)
 - Add loading the calibrated values from user space in
   tasdev_load_calibrated_data
 - Correct no dsp no work, it can still work in bypass mode.
 - Add calibrated register setting for tas2563&tas2781.
 - Add mutex into each kcontrol.
 - rename tas2781_force_fwload_get, tas2781_force_fwload_put-->
   tasdev_force_fwload_get, tasdev_force_fwload_put
 - rename tas2781_codec --> tasdev_codec
 - define tas2781_snd_control and tas2563_snd_control for volume
 - define tas2781_cali_control and tas2563_cali_control for calibration
---
 include/sound/tas2781-dsp.h       |   1 +
 include/sound/tas2781.h           |  92 ++-
 sound/soc/codecs/tas2781-comlib.c |  35 +-
 sound/soc/codecs/tas2781-fmwlib.c |  51 +-
 sound/soc/codecs/tas2781-i2c.c    | 941 +++++++++++++++++++++++++++++-
 5 files changed, 1061 insertions(+), 59 deletions(-)

Comments

Andy Shevchenko May 22, 2024, 12:01 p.m. UTC | #1
On Wed, May 22, 2024 at 07:29:41PM +0800, Shenghao Ding wrote:
> Calibrated data will be set to default after loading DSP config params,
> which will cause speaker protection work abnormally. Reload calibrated
> data after loading DSP config params.

...

> -// tas2781-lib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers
> +// tas2781-comlib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers

Please, drop the filename from the file completely, this change is exactly the
answer to "why having filename in the file is a bad idea in a long-term".

...

> +int tasdevice_chn_switch(struct tasdevice_priv *tas_priv,
> +	unsigned short chn)

Pretty much can be on a single line.

> +{
> +	struct i2c_client *client = (struct i2c_client *)tas_priv->client;
> +	struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
> +	struct regmap *map = tas_priv->regmap;
> +	int ret;

> +	if (client->addr != tasdev->dev_addr) {

With inverted check this entire function becomes neater.

> +		client->addr = tasdev->dev_addr;
> +		/* All devices share the same regmap, clear the page
> +		 * inside regmap once switching to another device.
> +		 * Register 0 at any pages and any books inside tas2781
> +		 * is the same one for page-switching.
> +		 */
> +		ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
> +		if (ret < 0) {
> +			dev_err(tas_priv->dev, "%s, E=%d\n",
> +				__func__, ret);
> +			return ret;
> +		}
> +		return 1;
> +	}
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(tasdevice_chn_switch);

Is it namespaced? If not would be good to make it so.
Also I see that other file uses namespaced exports.

...

> +	if (!priv->is_user_space_calidata &&
> +		cal_fmw) {

With all possible restrictions this can be on a single line besides the fact
that the second one currently has a broken indentation.

...

> +				&(data[k + 4 * j]), 4);

How parentheses are helpful here?

> +	}

...

> +#include <asm/unaligned.h>

linux/* followed by asm/* as the latter is not so generic as the former.

>  #include <linux/crc8.h>
>  #include <linux/firmware.h>
>  #include <linux/gpio/consumer.h>

...

> +	{
> +		.reg = TAS2781_PRM_TEST_57_REG,
> +		.val = { 0x14 },
> +		.val_len = 1,
> +		.is_locked = true

Here and everywhere else in cases like this (when it's not a termination line)
leave the trailing comma. It will reduce the churn in case this needs to be
expanded in the future.

> +	},

...

> +	int rc;
> +
> +	mutex_lock(&tas_priv->codec_lock);
> +	rc = tasdevice_digital_getvol(tas_priv, ucontrol, mc);
> +	mutex_unlock(&tas_priv->codec_lock);
>  
> -	return tasdevice_digital_getvol(tas_priv, ucontrol, mc);
> +	return rc;

Why not converting to use cleanup.h and this will become a oneliner update.
Same Q to all these mutex additions.

...

> +{
> +	struct i2c_client *clt = (struct i2c_client *)tas_priv->client;

Hmm... Why explicit casting? Is the client not void * or the same type?

> +	struct tasdevice *tasdev = tas_priv->tasdevice;

> +	int rc = -1;

Use proper error codes.

> +	int i;
> +
> +	if (data_len != 4)
> +		return rc;
> +
> +	for (i = 0; i < tas_priv->ndev; i++) {
> +		if (clt->addr == tasdev[i].dev_addr) {
> +			/* First byte is the device index. */
> +			dst[0] = i;
> +			tasdevice_dev_bulk_read(tas_priv, i, reg, &dst[1],
> +				4);

On one line this will be better to read.

> +			rc = 0;
> +			break;

Why not simply

			return 0;

?

> +		}
> +	}
> +
> +	return rc;
> +}

...

> +	if (tas_priv->chip_id != TAS2781 &&
> +		bytes_ext->max != 8 * tas_priv->ndev) {

Here and seems in many places you have broken indentation.

> +		rc = -1;

error code?

> +		goto out;
> +	}

> +		for (j = 0; j < sum; j++) {

With a temporary variable for p[j]...

> +			if (p[j].val_len == 1) {
> +				if (p[j].is_locked)
> +					tasdevice_dev_write(tas_priv, i,
> +						TAS2781_TEST_UNLOCK_REG,
> +						TAS2781_TEST_PAGE_UNLOCK);
> +				tasdevice_dev_read(tas_priv, i, p[j].reg,
> +					(int *)&p[j].val[0]);
> +			} else
> +				tasdevice_dev_bulk_read(tas_priv, i, p[j].reg,
> +					p[j].val, 4);

...all the above can be made more readable.

> +		}
> +
> +		for (j = 0; j < sum - 2; j++) {

Ditto for tas2781_cali_start_reg[j]

> +		}

...

> +	while (r > 1 + l) {
> +		mid = (l + r) / 2;
> +		ar_mid = get_unaligned_be32(tas2563_dvc_table[mid]);
> +		if (target < ar_mid)
> +			r = mid;
> +		else
> +			l = mid;
> +	}

Hmm... I'm wondering if bsearch() can be utilised here.

...

> +	ucontrol->value.integer.value[0] =
> +		abs(target - ar_l) <= abs(target - ar_r) ? l : r;

I don't understand why do you need 'target' to be in this check.

...

> +	uinfo->value.integer.max = (int)tas_priv->ndev - 1;

Why casting?

...

> +	scnprintf(active_dev_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,

Why 'c' variant in use? You are ignoring the returned value. Isn't strscpy()
you want or memtostr() (in both cases 2 parameters variant)?

> +		"Activate Tasdevice Id");

Same Q to all scnprintf() calls.

...

> +	cali_data->data = devm_kzalloc(tas_priv->dev, tas_priv->ndev *
> +		(cali_data->reg_array_sz * 4 + 1), GFP_KERNEL);

No way. First of all, we have kcalloc(), second, there is an overflow.h that
has necessary macros to calculate sizes for memory allocations.

> +	if (!cali_data->data)
> +		return -ENOMEM;

...

> -	int ret = 0;
>  
> -	if (tas_priv->fw_state != TASDEVICE_DSP_FW_ALL_OK) {
> -		dev_err(tas_priv->dev, "DSP bin file not loaded\n");
> -		ret = -EINVAL;
> +	if (!(tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK ||
> +		tas_priv->fw_state == TASDEVICE_RCA_FW_OK)) {
> +		dev_err(tas_priv->dev, "Bin file not loaded\n");
> +		return -EINVAL;
>  	}
>  
> -	return ret;
> +	return 0;

This patch is a mess. Try to split out the different logical changes into
different patches.
Pierre-Louis Bossart May 22, 2024, 1:40 p.m. UTC | #2
Could you move this sort of renaming/editing to a first patch?

>  /*Software Reset */
> -#define TAS2781_REG_SWRESET		TASDEVICE_REG(0x0, 0X0, 0x01)
> -#define TAS2781_REG_SWRESET_RESET	BIT(0)
> +#define TASDEVICE_REG_SWRESET		TASDEVICE_REG(0x00, 0X00, 0x01)
> +#define TASDEVICE_REG_SWRESET_RESET	BIT(0)

> -// tas2781-lib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers
> +// tas2781-comlib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers
>  //
> -// Copyright 2023 Texas Instruments, Inc.
> +// Copyright 2023 - 2024 Texas Instruments, Inc.

> @@ -254,8 +281,8 @@ void tas2781_reset(struct tasdevice_priv *tas_dev)
>  	} else {
>  		for (i = 0; i < tas_dev->ndev; i++) {
>  			ret = tasdevice_dev_write(tas_dev, i,
> -				TAS2781_REG_SWRESET,
> -				TAS2781_REG_SWRESET_RESET);
> +				TASDEVICE_REG_SWRESET,
> +				TASDEVICE_REG_SWRESET_RESET);

> @@ -591,8 +1470,8 @@ static const struct snd_soc_component_driver
>  	soc_codec_driver_tasdevice = {
>  	.probe			= tasdevice_codec_probe,
>  	.remove			= tasdevice_codec_remove,
> -	.controls		= tas2781_snd_controls,
> -	.num_controls		= ARRAY_SIZE(tas2781_snd_controls),
> +	.controls		= tasdevice_snd_controls,
> +	.num_controls		= ARRAY_SIZE(tasdevice_snd_controls),
>  	.dapm_widgets		= tasdevice_dapm_widgets,
>  	.num_dapm_widgets	= ARRAY_SIZE(tasdevice_dapm_widgets),
>  	.dapm_routes		= tasdevice_audio_map,

And then add functional changes in a second patch? It'd be simpler to
review really...
kernel test robot May 22, 2024, 10:30 p.m. UTC | #3
Hi Shenghao,

kernel test robot noticed the following build errors:

[auto build test ERROR on broonie-sound/for-next]
[also build test ERROR on next-20240522]
[cannot apply to tiwai-sound/for-next tiwai-sound/for-linus linus/master v6.9]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Shenghao-Ding/ASoc-tas2781-Add-Calibration-Kcontrols-and-tas2563-digtial-gain-for-Chromebook/20240522-193315
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
patch link:    https://lore.kernel.org/r/20240522112942.994-1-shenghao-ding%40ti.com
patch subject: [PATCH v1] ASoc: tas2781: Add Calibration Kcontrols and tas2563 digtial gain for Chromebook
config: x86_64-buildonly-randconfig-002-20240523 (https://download.01.org/0day-ci/archive/20240523/202405230633.Vq1CHD6e-lkp@intel.com/config)
compiler: gcc-8 (Ubuntu 8.4.0-3ubuntu2) 8.4.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240523/202405230633.Vq1CHD6e-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202405230633.Vq1CHD6e-lkp@intel.com/

All errors (new ones prefixed by >>):

   sound/soc/codecs/tas2781-i2c.c: In function 'tas2563_digital_gain_get':
>> sound/soc/codecs/tas2781-i2c.c:696:31: error: 'tas2563_dvc_table' undeclared (first use in this function); did you mean 'tasklet_disable'?
      ar_mid = get_unaligned_be32(tas2563_dvc_table[mid]);
                                  ^~~~~~~~~~~~~~~~~
                                  tasklet_disable
   sound/soc/codecs/tas2781-i2c.c:696:31: note: each undeclared identifier is reported only once for each function it appears in
   sound/soc/codecs/tas2781-i2c.c: In function 'tas2563_digital_gain_put':
   sound/soc/codecs/tas2781-i2c.c:737:29: error: 'tas2563_dvc_table' undeclared (first use in this function); did you mean 'tasklet_disable'?
     volwr = get_unaligned_be32(tas2563_dvc_table[vol]);
                                ^~~~~~~~~~~~~~~~~
                                tasklet_disable
   In file included from sound/soc/codecs/tas2781-i2c.c:30:
   sound/soc/codecs/tas2781-i2c.c: At top level:
>> sound/soc/codecs/tas2781-i2c.c:790:3: error: 'tas2563_dvc_tlv' undeclared here (not in a function); did you mean 'tas2563_snd_controls'?
      tas2563_dvc_tlv),
      ^~~~~~~~~~~~~~~
   include/sound/soc.h:293:12: note: in definition of macro 'SOC_SINGLE_RANGE_EXT_TLV'
     .tlv.p = (tlv_array), \
               ^~~~~~~~~
>> sound/soc/codecs/tas2781-i2c.c:788:17: error: 'tas2563_dvc_table' undeclared here (not in a function); did you mean 'tas2563_snd_controls'?
      0, ARRAY_SIZE(tas2563_dvc_table) - 1, 0,
                    ^~~~~~~~~~~~~~~~~
   include/sound/soc.h:298:42: note: in definition of macro 'SOC_SINGLE_RANGE_EXT_TLV'
       .rshift = xshift, .min = xmin, .max = xmax, \
                                             ^~~~
   sound/soc/codecs/tas2781-i2c.c:788:6: note: in expansion of macro 'ARRAY_SIZE'
      0, ARRAY_SIZE(tas2563_dvc_table) - 1, 0,
         ^~~~~~~~~~
   include/linux/build_bug.h:16:51: error: bit-field '<anonymous>' width not an integer constant
    #define BUILD_BUG_ON_ZERO(e) ((int)(sizeof(struct { int:(-!!(e)); })))
                                                      ^
   include/sound/soc.h:298:42: note: in definition of macro 'SOC_SINGLE_RANGE_EXT_TLV'
       .rshift = xshift, .min = xmin, .max = xmax, \
                                             ^~~~
   include/linux/compiler.h:237:28: note: in expansion of macro 'BUILD_BUG_ON_ZERO'
    #define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
                               ^~~~~~~~~~~~~~~~~
   include/linux/array_size.h:11:59: note: in expansion of macro '__must_be_array'
    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
                                                              ^~~~~~~~~~~~~~~
   sound/soc/codecs/tas2781-i2c.c:788:6: note: in expansion of macro 'ARRAY_SIZE'
      0, ARRAY_SIZE(tas2563_dvc_table) - 1, 0,
         ^~~~~~~~~~


vim +696 sound/soc/codecs/tas2781-i2c.c

   669	
   670	static int tas2563_digital_gain_get(
   671		struct snd_kcontrol *kcontrol,
   672		struct snd_ctl_elem_value *ucontrol)
   673	{
   674		struct soc_mixer_control *mc =
   675			(struct soc_mixer_control *)kcontrol->private_value;
   676		struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
   677		struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
   678		unsigned int l = 0, r = mc->max;
   679		unsigned int target, ar_mid, mid, ar_l, ar_r;
   680		unsigned int reg = mc->reg;
   681		unsigned char data[4];
   682		int ret;
   683	
   684		mutex_lock(&tas_dev->codec_lock);
   685		/* Read the primary device */
   686		ret =  tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4);
   687		if (ret) {
   688			dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__);
   689			goto out;
   690		}
   691	
   692		target = get_unaligned_be32(&data[0]);
   693	
   694		while (r > 1 + l) {
   695			mid = (l + r) / 2;
 > 696			ar_mid = get_unaligned_be32(tas2563_dvc_table[mid]);
   697			if (target < ar_mid)
   698				r = mid;
   699			else
   700				l = mid;
   701		}
   702	
   703		ar_l = get_unaligned_be32(tas2563_dvc_table[l]);
   704		ar_r = get_unaligned_be32(tas2563_dvc_table[r]);
   705	
   706		ucontrol->value.integer.value[0] =
   707			abs(target - ar_l) <= abs(target - ar_r) ? l : r;
   708	out:
   709		mutex_unlock(&tas_dev->codec_lock);
   710		return 0;
   711	}
   712	
   713	static int tas2563_digital_gain_put(
   714		struct snd_kcontrol *kcontrol,
   715		struct snd_ctl_elem_value *ucontrol)
   716	{
   717		struct soc_mixer_control *mc =
   718			(struct soc_mixer_control *)kcontrol->private_value;
   719		struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
   720		struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
   721		unsigned int reg = mc->reg;
   722		unsigned int volrd, volwr;
   723		int vol = ucontrol->value.integer.value[0];
   724		int max = mc->max, i, ret = 1;
   725		unsigned char data[4];
   726	
   727		vol = clamp(vol, 0, max);
   728		mutex_lock(&tas_dev->codec_lock);
   729		/* Read the primary device */
   730		ret =  tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4);
   731		if (ret) {
   732			dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__);
   733			goto out;
   734		}
   735	
   736		volrd = get_unaligned_be32(&data[0]);
   737		volwr = get_unaligned_be32(tas2563_dvc_table[vol]);
   738	
   739		if (volrd == volwr) {
   740			ret = 0;
   741			goto out;
   742		}
   743	
   744		for (i = 0; i < tas_dev->ndev; i++) {
   745			ret = tasdevice_dev_bulk_write(tas_dev, i, reg,
   746				(unsigned char *)tas2563_dvc_table[vol], 4);
   747			if (ret)
   748				dev_err(tas_dev->dev,
   749					"%s, set digital vol error in device %d\n",
   750					__func__, i);
   751		}
   752	
   753	out:
   754		mutex_unlock(&tas_dev->codec_lock);
   755		return ret;
   756	}
   757	
   758	static const struct snd_kcontrol_new tasdevice_snd_controls[] = {
   759		SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0,
   760			tasdev_force_fwload_get, tasdev_force_fwload_put),
   761	};
   762	
   763	static const struct snd_kcontrol_new tasdevice_cali_controls[] = {
   764		SOC_SINGLE_EXT("Calibration Stop", SND_SOC_NOPM, 0, 1, 0,
   765			tasdev_nop_get, tasdev_calib_stop_put),
   766		SND_SOC_BYTES_EXT("Amp TF Data", 5, tasdev_tf_data_get, NULL),
   767		SND_SOC_BYTES_EXT("Amp RE Data", 5, tasdev_re_data_get, NULL),
   768		SND_SOC_BYTES_EXT("Amp R0 Data", 5, tasdev_r0_data_get, NULL),
   769		SND_SOC_BYTES_EXT("Amp XMA1 Data", 5, tasdev_XMA1_data_get, NULL),
   770		SND_SOC_BYTES_EXT("Amp XMA2 Data", 5, tasdev_XMA2_data_get, NULL),
   771	};
   772	
   773	static const struct snd_kcontrol_new tas2781_snd_controls[] = {
   774		SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL,
   775			1, 0, 20, 0, tas2781_amp_getvol,
   776			tas2781_amp_putvol, amp_vol_tlv),
   777		SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL,
   778			0, 0, 200, 1, tas2781_digital_getvol,
   779			tas2781_digital_putvol, dvc_tlv),
   780	};
   781	
   782	static const struct snd_kcontrol_new tas2781_cali_controls[] = {
   783		SND_SOC_BYTES_EXT("Amp Latch Data", 2, tas2781_latch_reg_get, NULL),
   784	};
   785	
   786	static const struct snd_kcontrol_new tas2563_snd_controls[] = {
   787		SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2563_DVC_LVL, 0,
 > 788			0, ARRAY_SIZE(tas2563_dvc_table) - 1, 0,
   789			tas2563_digital_gain_get, tas2563_digital_gain_put,
 > 790			tas2563_dvc_tlv),
   791	};
   792
kernel test robot May 23, 2024, 12:34 a.m. UTC | #4
Hi Shenghao,

kernel test robot noticed the following build errors:

[auto build test ERROR on broonie-sound/for-next]
[also build test ERROR on next-20240522]
[cannot apply to tiwai-sound/for-next tiwai-sound/for-linus linus/master v6.9]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Shenghao-Ding/ASoc-tas2781-Add-Calibration-Kcontrols-and-tas2563-digtial-gain-for-Chromebook/20240522-193315
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
patch link:    https://lore.kernel.org/r/20240522112942.994-1-shenghao-ding%40ti.com
patch subject: [PATCH v1] ASoc: tas2781: Add Calibration Kcontrols and tas2563 digtial gain for Chromebook
config: mips-randconfig-r081-20240523 (https://download.01.org/0day-ci/archive/20240523/202405230838.bJI1Z9EJ-lkp@intel.com/config)
compiler: clang version 16.0.6 (https://github.com/llvm/llvm-project 7cbf1a2591520c2491aa35339f227775f4d3adf6)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240523/202405230838.bJI1Z9EJ-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202405230838.bJI1Z9EJ-lkp@intel.com/

All errors (new ones prefixed by >>):

>> sound/soc/codecs/tas2781-i2c.c:696:31: error: use of undeclared identifier 'tas2563_dvc_table'
                   ar_mid = get_unaligned_be32(tas2563_dvc_table[mid]);
                                               ^
   sound/soc/codecs/tas2781-i2c.c:703:28: error: use of undeclared identifier 'tas2563_dvc_table'
           ar_l = get_unaligned_be32(tas2563_dvc_table[l]);
                                     ^
   sound/soc/codecs/tas2781-i2c.c:704:28: error: use of undeclared identifier 'tas2563_dvc_table'
           ar_r = get_unaligned_be32(tas2563_dvc_table[r]);
                                     ^
   sound/soc/codecs/tas2781-i2c.c:737:29: error: use of undeclared identifier 'tas2563_dvc_table'
           volwr = get_unaligned_be32(tas2563_dvc_table[vol]);
                                      ^
   sound/soc/codecs/tas2781-i2c.c:746:21: error: use of undeclared identifier 'tas2563_dvc_table'
                           (unsigned char *)tas2563_dvc_table[vol], 4);
                                            ^
>> sound/soc/codecs/tas2781-i2c.c:790:3: error: use of undeclared identifier 'tas2563_dvc_tlv'
                   tas2563_dvc_tlv),
                   ^
   sound/soc/codecs/tas2781-i2c.c:788:17: error: use of undeclared identifier 'tas2563_dvc_table'
                   0, ARRAY_SIZE(tas2563_dvc_table) - 1, 0,
                                 ^
   sound/soc/codecs/tas2781-i2c.c:788:17: error: use of undeclared identifier 'tas2563_dvc_table'
   sound/soc/codecs/tas2781-i2c.c:788:17: error: use of undeclared identifier 'tas2563_dvc_table'
   sound/soc/codecs/tas2781-i2c.c:1433:4: error: invalid application of 'sizeof' to an incomplete type 'const struct snd_kcontrol_new[]'
                           ARRAY_SIZE(tas2563_snd_controls));
                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/array_size.h:11:32: note: expanded from macro 'ARRAY_SIZE'
   #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
                                  ^~~~~
   10 errors generated.


vim +/tas2563_dvc_table +696 sound/soc/codecs/tas2781-i2c.c

   669	
   670	static int tas2563_digital_gain_get(
   671		struct snd_kcontrol *kcontrol,
   672		struct snd_ctl_elem_value *ucontrol)
   673	{
   674		struct soc_mixer_control *mc =
   675			(struct soc_mixer_control *)kcontrol->private_value;
   676		struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
   677		struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
   678		unsigned int l = 0, r = mc->max;
   679		unsigned int target, ar_mid, mid, ar_l, ar_r;
   680		unsigned int reg = mc->reg;
   681		unsigned char data[4];
   682		int ret;
   683	
   684		mutex_lock(&tas_dev->codec_lock);
   685		/* Read the primary device */
   686		ret =  tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4);
   687		if (ret) {
   688			dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__);
   689			goto out;
   690		}
   691	
   692		target = get_unaligned_be32(&data[0]);
   693	
   694		while (r > 1 + l) {
   695			mid = (l + r) / 2;
 > 696			ar_mid = get_unaligned_be32(tas2563_dvc_table[mid]);
   697			if (target < ar_mid)
   698				r = mid;
   699			else
   700				l = mid;
   701		}
   702	
   703		ar_l = get_unaligned_be32(tas2563_dvc_table[l]);
   704		ar_r = get_unaligned_be32(tas2563_dvc_table[r]);
   705	
   706		ucontrol->value.integer.value[0] =
   707			abs(target - ar_l) <= abs(target - ar_r) ? l : r;
   708	out:
   709		mutex_unlock(&tas_dev->codec_lock);
   710		return 0;
   711	}
   712	
   713	static int tas2563_digital_gain_put(
   714		struct snd_kcontrol *kcontrol,
   715		struct snd_ctl_elem_value *ucontrol)
   716	{
   717		struct soc_mixer_control *mc =
   718			(struct soc_mixer_control *)kcontrol->private_value;
   719		struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
   720		struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
   721		unsigned int reg = mc->reg;
   722		unsigned int volrd, volwr;
   723		int vol = ucontrol->value.integer.value[0];
   724		int max = mc->max, i, ret = 1;
   725		unsigned char data[4];
   726	
   727		vol = clamp(vol, 0, max);
   728		mutex_lock(&tas_dev->codec_lock);
   729		/* Read the primary device */
   730		ret =  tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4);
   731		if (ret) {
   732			dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__);
   733			goto out;
   734		}
   735	
   736		volrd = get_unaligned_be32(&data[0]);
   737		volwr = get_unaligned_be32(tas2563_dvc_table[vol]);
   738	
   739		if (volrd == volwr) {
   740			ret = 0;
   741			goto out;
   742		}
   743	
   744		for (i = 0; i < tas_dev->ndev; i++) {
   745			ret = tasdevice_dev_bulk_write(tas_dev, i, reg,
   746				(unsigned char *)tas2563_dvc_table[vol], 4);
   747			if (ret)
   748				dev_err(tas_dev->dev,
   749					"%s, set digital vol error in device %d\n",
   750					__func__, i);
   751		}
   752	
   753	out:
   754		mutex_unlock(&tas_dev->codec_lock);
   755		return ret;
   756	}
   757	
   758	static const struct snd_kcontrol_new tasdevice_snd_controls[] = {
   759		SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0,
   760			tasdev_force_fwload_get, tasdev_force_fwload_put),
   761	};
   762	
   763	static const struct snd_kcontrol_new tasdevice_cali_controls[] = {
   764		SOC_SINGLE_EXT("Calibration Stop", SND_SOC_NOPM, 0, 1, 0,
   765			tasdev_nop_get, tasdev_calib_stop_put),
   766		SND_SOC_BYTES_EXT("Amp TF Data", 5, tasdev_tf_data_get, NULL),
   767		SND_SOC_BYTES_EXT("Amp RE Data", 5, tasdev_re_data_get, NULL),
   768		SND_SOC_BYTES_EXT("Amp R0 Data", 5, tasdev_r0_data_get, NULL),
   769		SND_SOC_BYTES_EXT("Amp XMA1 Data", 5, tasdev_XMA1_data_get, NULL),
   770		SND_SOC_BYTES_EXT("Amp XMA2 Data", 5, tasdev_XMA2_data_get, NULL),
   771	};
   772	
   773	static const struct snd_kcontrol_new tas2781_snd_controls[] = {
   774		SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL,
   775			1, 0, 20, 0, tas2781_amp_getvol,
   776			tas2781_amp_putvol, amp_vol_tlv),
   777		SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL,
   778			0, 0, 200, 1, tas2781_digital_getvol,
   779			tas2781_digital_putvol, dvc_tlv),
   780	};
   781	
   782	static const struct snd_kcontrol_new tas2781_cali_controls[] = {
   783		SND_SOC_BYTES_EXT("Amp Latch Data", 2, tas2781_latch_reg_get, NULL),
   784	};
   785	
   786	static const struct snd_kcontrol_new tas2563_snd_controls[] = {
   787		SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2563_DVC_LVL, 0,
   788			0, ARRAY_SIZE(tas2563_dvc_table) - 1, 0,
   789			tas2563_digital_gain_get, tas2563_digital_gain_put,
 > 790			tas2563_dvc_tlv),
   791	};
   792
Shenghao Ding June 24, 2024, 11:42 a.m. UTC | #5
Hi Andy
Thanks for your comments. Following are my answers.

> -----Original Message-----
> From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Sent: Wednesday, May 22, 2024 8:02 PM
> To: Ding, Shenghao <shenghao-ding@ti.com>
> Cc: broonie@kernel.org; lgirdwood@gmail.com; perex@perex.cz; pierre-
> louis.bossart@linux.intel.com; 13916275206@139.com;
> judyhsiao@google.com; alsa-devel@alsa-project.org; Salazar, Ivan <i-
> salazar@ti.com>; linux-kernel@vger.kernel.org; Chadha, Jasjot Singh <j-
> chadha@ti.com>; liam.r.girdwood@intel.com; bard.liao@intel.com; yung-
> chuan.liao@linux.intel.com; Rao, Dipa <dipa@ti.com>; Lu, Kevin <kevin-
> lu@ti.com>; yuhsuan@google.com; tiwai@suse.de; Xu, Baojun
> <baojun.xu@ti.com>; soyer@irl.hu; Baojun.Xu@fpt.com; Navada Kanyana,
> Mukund <navada@ti.com>; cujomalainey@google.com; Kutty, Aanya
> <aanya@ti.com>; Mahmud, Nayeem <nayeem.mahmud@ti.com>
> Subject: [EXTERNAL] Re: [PATCH v1] ASoc: tas2781: Add Calibration Kcontrols
> and tas2563 digtial gain for Chromebook
> 
> On Wed, May 22, 2024 at 07: 29: 41PM +0800, Shenghao Ding wrote: >
> Calibrated data will be set to default after loading DSP config params, >
> which will cause speaker protection work abnormally. Reload calibrated >
> data after loading ZjQcmQRYFpfptBannerStart This message was sent from
> outside of Texas Instruments.
> Do not click links or open attachments unless you recognize the source of this
> email and know the content is safe. If you wish to report this message to IT
> Security, please forward the message as an attachment to
> phishing@list.ti.com
> 
........................
> > +	while (r > 1 + l) {
> > +		mid = (l + r) / 2;
> > +		ar_mid = get_unaligned_be32(tas2563_dvc_table[mid]);
> > +		if (target < ar_mid)
> > +			r = mid;
> > +		else
> > +			l = mid;
> > +	}
> 
> Hmm... I'm wondering if bsearch() can be utilised here.
Bsearch is to find out the value, in this function is to find out the member same as or closer to the input value.

> 
> ...
> 
> > +	ucontrol->value.integer.value[0] =
> > +		abs(target - ar_l) <= abs(target - ar_r) ? l : r;
> 
> I don't understand why do you need 'target' to be in this check.
> 
> ...
> 
> > +	uinfo->value.integer.max = (int)tas_priv->ndev - 1;
> 
> Why casting?
> 
> ...
> 
> > +	scnprintf(active_dev_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
> 
> Why 'c' variant in use? You are ignoring the returned value. Isn't strscpy() you
> want or memtostr() (in both cases 2 parameters variant)?
> 
> > +		"Activate Tasdevice Id");
> 
> Same Q to all scnprintf() calls.
> 
> ...
> 
> > +	cali_data->data = devm_kzalloc(tas_priv->dev, tas_priv->ndev *
> > +		(cali_data->reg_array_sz * 4 + 1), GFP_KERNEL);
> 
> No way. First of all, we have kcalloc(), second, there is an overflow.h that has
> necessary macros to calculate sizes for memory allocations.
Memory allocated with devm_kzalloc is automatically freed on driver detach while kcalloc can’t
> 
> > +	if (!cali_data->data)
> > +		return -ENOMEM;
> 
> ...

BR
Shenghao Ding
Andy Shevchenko Aug. 9, 2024, 2:36 p.m. UTC | #6
On Mon, Jun 24, 2024 at 11:42:11AM +0000, Ding, Shenghao wrote:
> > From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> > Sent: Wednesday, May 22, 2024 8:02 PM
> > To: Ding, Shenghao <shenghao-ding@ti.com>
> > On Wed, May 22, 2024 at 07: 29: 41PM +0800, Shenghao Ding wrote: >


(some comments were not answered, are you agree on all of the points?)

...

> > > +	cali_data->data = devm_kzalloc(tas_priv->dev, tas_priv->ndev *
> > > +		(cali_data->reg_array_sz * 4 + 1), GFP_KERNEL);
> > 
> > No way. First of all, we have kcalloc(), second, there is an overflow.h that has
> > necessary macros to calculate sizes for memory allocations.
> Memory allocated with devm_kzalloc is automatically freed on driver detach while kcalloc can’t

Yes, we have devm variant for kcalloc(), why can it be not utilised?

> > > +	if (!cali_data->data)
> > > +		return -ENOMEM;
diff mbox series

Patch

diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h
index 7fba7ea26a4b..74ab6f113029 100644
--- a/include/sound/tas2781-dsp.h
+++ b/include/sound/tas2781-dsp.h
@@ -121,6 +121,7 @@  enum tasdevice_dsp_fw_state {
 	TASDEVICE_DSP_FW_NONE = 0,
 	TASDEVICE_DSP_FW_PENDING,
 	TASDEVICE_DSP_FW_FAIL,
+	TASDEVICE_RCA_FW_OK,
 	TASDEVICE_DSP_FW_ALL_OK,
 };
 
diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h
index 99ca3e401fd1..2fb3d03d0040 100644
--- a/include/sound/tas2781.h
+++ b/include/sound/tas2781.h
@@ -2,7 +2,7 @@ 
 //
 // ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier
 //
-// Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+// Copyright (C) 2022 - 2024 Texas Instruments Incorporated
 // https://www.ti.com
 //
 // The TAS2563/TAS2781 driver implements a flexible and configurable
@@ -23,6 +23,8 @@ 
 #define SMARTAMP_MODULE_NAME		"tas2781"
 #define TAS2781_GLOBAL_ADDR	0x40
 #define TAS2563_GLOBAL_ADDR	0x48
+#define CAL_DAT_SZ		20
+
 #define TASDEVICE_RATES			(SNDRV_PCM_RATE_44100 |\
 	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
 	SNDRV_PCM_RATE_88200)
@@ -31,6 +33,11 @@ 
 	SNDRV_PCM_FMTBIT_S24_LE | \
 	SNDRV_PCM_FMTBIT_S32_LE)
 
+#define TASDEVICE_CMD_SING_W		0x1
+#define TASDEVICE_CMD_BURST		0x2
+#define TASDEVICE_CMD_DELAY		0x3
+#define TASDEVICE_CMD_FIELD_W		0x4
+
 /*PAGE Control Register (available in page0 of each book) */
 #define TASDEVICE_PAGE_SELECT		0x00
 #define TASDEVICE_BOOKCTL_PAGE		0x00
@@ -43,21 +50,73 @@ 
 					(page * 128)) + reg)
 
 /*Software Reset */
-#define TAS2781_REG_SWRESET		TASDEVICE_REG(0x0, 0X0, 0x01)
-#define TAS2781_REG_SWRESET_RESET	BIT(0)
+#define TASDEVICE_REG_SWRESET		TASDEVICE_REG(0x00, 0X00, 0x01)
+#define TASDEVICE_REG_SWRESET_RESET	BIT(0)
 
 /*I2C Checksum */
-#define TASDEVICE_I2CChecksum		TASDEVICE_REG(0x0, 0x0, 0x7E)
+#define TASDEVICE_I2CChecksum		TASDEVICE_REG(0x00, 0x00, 0x7E)
+
+/* XM_340 */
+#define	TASDEVICE_XM_A1_REG	TASDEVICE_REG(0x64, 0x02, 0x4c)
+/* XM_341 */
+#define	TASDEVICE_XM_A2_REG	TASDEVICE_REG(0x64, 0x02, 0x64)
 
 /* Volume control */
-#define TAS2781_DVC_LVL			TASDEVICE_REG(0x0, 0x0, 0x1A)
-#define TAS2781_AMP_LEVEL		TASDEVICE_REG(0x0, 0x0, 0x03)
+#define TAS2563_DVC_LVL			TASDEVICE_REG(0x00, 0x02, 0x0C)
+#define TAS2781_DVC_LVL			TASDEVICE_REG(0x00, 0x00, 0x1A)
+#define TAS2781_AMP_LEVEL		TASDEVICE_REG(0x00, 0x00, 0x03)
 #define TAS2781_AMP_LEVEL_MASK		GENMASK(5, 1)
 
-#define TASDEVICE_CMD_SING_W		0x1
-#define TASDEVICE_CMD_BURST		0x2
-#define TASDEVICE_CMD_DELAY		0x3
-#define TASDEVICE_CMD_FIELD_W		0x4
+#define TAS2563_PRM_R0_REG		TASDEVICE_REG(0x00, 0x0f, 0x34)
+#define TAS2563_PRM_R0_LOW_REG		TASDEVICE_REG(0x00, 0x0f, 0x48)
+#define TAS2563_PRM_INVR0_REG		TASDEVICE_REG(0x00, 0x0f, 0x40)
+#define TAS2563_PRM_POW_REG		TASDEVICE_REG(0x00, 0x0d, 0x3c)
+#define TAS2563_PRM_TLIMIT_REG		TASDEVICE_REG(0x00, 0x10, 0x14)
+
+#define TAS2563_RUNTIME_RE_REG_TF	TASDEVICE_REG(0x64, 0x02, 0x70)
+#define TAS2563_RUNTIME_RE_REG		TASDEVICE_REG(0x64, 0x02, 0x48)
+
+#define TAS2563_PRM_ENFF_REG		TASDEVICE_REG(0x00, 0x0d, 0x54)
+#define TAS2563_PRM_DISTCK_REG		TASDEVICE_REG(0x00, 0x0d, 0x58)
+#define TAS2563_PRM_TE_SCTHR_REG	TASDEVICE_REG(0x00, 0x0f, 0x60)
+#define TAS2563_PRM_PLT_FLAG_REG	TASDEVICE_REG(0x00, 0x0d, 0x74)
+#define TAS2563_PRM_SINEGAIN_REG	TASDEVICE_REG(0x00, 0x0d, 0x7c)
+/* prm_Int_B0 */
+#define TAS2563_TE_TA1_REG		TASDEVICE_REG(0x00, 0x10, 0x0c)
+/* prm_Int_A1 */
+#define TAS2563_TE_TA1_AT_REG		TASDEVICE_REG(0x00, 0x10, 0x10)
+/* prm_TE_Beta */
+#define TAS2563_TE_TA2_REG		TASDEVICE_REG(0x00, 0x0f, 0x64)
+/* prm_TE_Beta1 */
+#define TAS2563_TE_AT_REG		TASDEVICE_REG(0x00, 0x0f, 0x68)
+/* prm_TE_1_Beta1 */
+#define TAS2563_TE_DT_REG		TASDEVICE_REG(0x00, 0x0f, 0x70)
+
+#define TAS2781_PRM_R0_REG		TASDEVICE_REG(0x00, 0x17, 0x74)
+#define TAS2781_PRM_R0_LOW_REG		TASDEVICE_REG(0x00, 0x18, 0x0c)
+#define TAS2781_PRM_INVR0_REG		TASDEVICE_REG(0x00, 0x18, 0x14)
+#define TAS2781_PRM_POW_REG		TASDEVICE_REG(0x00, 0x13, 0x70)
+#define TAS2781_PRM_TLIMIT_REG		TASDEVICE_REG(0x00, 0x18, 0x7c)
+
+#define TAS2781_PRM_INT_MASK_REG	TASDEVICE_REG(0x00, 0x00, 0x3b)
+#define TAS2781_PRM_CLK_CFG_REG		TASDEVICE_REG(0x00, 0x00, 0x5c)
+#define TAS2781_PRM_RSVD_REG		TASDEVICE_REG(0x00, 0x01, 0x19)
+#define TAS2781_PRM_TEST_57_REG		TASDEVICE_REG(0x00, 0xfd, 0x39)
+#define TAS2781_PRM_TEST_62_REG		TASDEVICE_REG(0x00, 0xfd, 0x3e)
+#define TAS2781_PRM_PVDD_UVLO_REG	TASDEVICE_REG(0x00, 0x00, 0x71)
+#define TAS2781_PRM_CHNL_0_REG		TASDEVICE_REG(0x00, 0x00, 0x03)
+#define TAS2781_PRM_NG_CFG0_REG		TASDEVICE_REG(0x00, 0x00, 0x35)
+#define TAS2781_PRM_IDLE_CH_DET_REG	TASDEVICE_REG(0x00, 0x00, 0x66)
+#define TAS2781_PRM_PLT_FLAG_REG	TASDEVICE_REG(0x00, 0x14, 0x38)
+#define TAS2781_PRM_SINEGAIN_REG	TASDEVICE_REG(0x00, 0x14, 0x40)
+#define TAS2781_PRM_SINEGAIN2_REG	TASDEVICE_REG(0x00, 0x14, 0x44)
+
+#define TAS2781_TEST_UNLOCK_REG		TASDEVICE_REG(0x00, 0xFD, 0x0D)
+#define TAS2781_TEST_PAGE_UNLOCK	0x0D
+
+#define TAS2781_RUNTIME_LATCH_RE_REG	TASDEVICE_REG(0x00, 0x00, 0x49)
+#define TAS2781_RUNTIME_RE_REG_TF	TASDEVICE_REG(0x64, 0x62, 0x48)
+#define TAS2781_RUNTIME_RE_REG		TASDEVICE_REG(0x64, 0x63, 0x44)
 
 enum audio_device {
 	TAS2563,
@@ -69,7 +128,15 @@  enum device_catlog_id {
 	OTHERS
 };
 
+struct bulk_reg_val {
+	int reg;
+	unsigned char val[4];
+	unsigned char val_len;
+	bool is_locked;
+};
+
 struct tasdevice {
+	struct bulk_reg_val *cali_data_restore;
 	struct tasdevice_fw *cali_data_fmw;
 	unsigned int dev_addr;
 	unsigned int err_code;
@@ -88,6 +155,8 @@  struct tasdevice_irqinfo {
 struct calidata {
 	unsigned char *data;
 	unsigned long total_sz;
+	unsigned int *reg_array;
+	unsigned int reg_array_sz;
 };
 
 struct tasdevice_priv {
@@ -122,6 +191,7 @@  struct tasdevice_priv {
 	bool force_fwload_status;
 	bool playback_started;
 	bool isacpi;
+	bool is_user_space_calidata;
 	unsigned int global_addr;
 
 	int (*fw_parse_variable_header)(struct tasdevice_priv *tas_priv,
@@ -148,6 +218,8 @@  int tasdevice_init(struct tasdevice_priv *tas_priv);
 void tasdevice_remove(struct tasdevice_priv *tas_priv);
 int tasdevice_save_calibration(struct tasdevice_priv *tas_priv);
 void tasdevice_apply_calibration(struct tasdevice_priv *tas_priv);
+int tasdevice_chn_switch(struct tasdevice_priv *tas_priv,
+	unsigned short chn);
 int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
 	unsigned short chn, unsigned int reg, unsigned int *value);
 int tasdevice_dev_write(struct tasdevice_priv *tas_priv,
diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c
index 3aa81514dad7..9d7b56663b6b 100644
--- a/sound/soc/codecs/tas2781-comlib.c
+++ b/sound/soc/codecs/tas2781-comlib.c
@@ -1,8 +1,8 @@ 
 // SPDX-License-Identifier: GPL-2.0
 //
-// tas2781-lib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers
+// tas2781-comlib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers
 //
-// Copyright 2023 Texas Instruments, Inc.
+// Copyright 2023 - 2024 Texas Instruments, Inc.
 //
 // Author: Shenghao Ding <shenghao-ding@ti.com>
 
@@ -89,6 +89,33 @@  static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv,
 	return ret;
 }
 
+int tasdevice_chn_switch(struct tasdevice_priv *tas_priv,
+	unsigned short chn)
+{
+	struct i2c_client *client = (struct i2c_client *)tas_priv->client;
+	struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
+	struct regmap *map = tas_priv->regmap;
+	int ret;
+
+	if (client->addr != tasdev->dev_addr) {
+		client->addr = tasdev->dev_addr;
+		/* All devices share the same regmap, clear the page
+		 * inside regmap once switching to another device.
+		 * Register 0 at any pages and any books inside tas2781
+		 * is the same one for page-switching.
+		 */
+		ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
+		if (ret < 0) {
+			dev_err(tas_priv->dev, "%s, E=%d\n",
+				__func__, ret);
+			return ret;
+		}
+		return 1;
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tasdevice_chn_switch);
+
 int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
 	unsigned short chn, unsigned int reg, unsigned int *val)
 {
@@ -254,8 +281,8 @@  void tas2781_reset(struct tasdevice_priv *tas_dev)
 	} else {
 		for (i = 0; i < tas_dev->ndev; i++) {
 			ret = tasdevice_dev_write(tas_dev, i,
-				TAS2781_REG_SWRESET,
-				TAS2781_REG_SWRESET_RESET);
+				TASDEVICE_REG_SWRESET,
+				TASDEVICE_REG_SWRESET_RESET);
 			if (ret < 0)
 				dev_err(tas_dev->dev,
 					"dev %d swreset fail, %d\n",
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
index 265a8ca25cbb..19a5133083dc 100644
--- a/sound/soc/codecs/tas2781-fmwlib.c
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -2153,20 +2153,39 @@  static int tasdevice_load_data(struct tasdevice_priv *tas_priv,
 
 static void tasdev_load_calibrated_data(struct tasdevice_priv *priv, int i)
 {
+	struct tasdevice_fw *cal_fmw = priv->tasdevice[i].cali_data_fmw;
+	struct calidata *cali_data = &priv->cali_data;
+	unsigned char *data = cali_data->data;
 	struct tasdevice_calibration *cal;
-	struct tasdevice_fw *cal_fmw;
+	int k = i * (CAL_DAT_SZ + 1);
+	int j, rc;
 
-	cal_fmw = priv->tasdevice[i].cali_data_fmw;
+	if (!priv->is_user_space_calidata &&
+		cal_fmw) {
+		cal = cal_fmw->calibrations;
 
-	/* No calibrated data for current devices, playback will go ahead. */
-	if (!cal_fmw)
+		if (cal)
+			load_calib_data(priv, &cal->dev_data);
 		return;
-
-	cal = cal_fmw->calibrations;
-	if (cal)
+	}
+	if (!priv->is_user_space_calidata)
 		return;
-
-	load_calib_data(priv, &cal->dev_data);
+	/* load calibrated data from user space */
+	if (data[k] != i) {
+		dev_err(priv->dev, "%s: no cal-data for dev %d from usr-spc\n",
+			__func__, i);
+		return;
+	}
+	for (j = 0; j < cali_data->reg_array_sz; j++) {
+		if (data[k] != j)
+			rc = tasdevice_dev_bulk_write(priv, i,
+				cali_data->reg_array[j],
+				&(data[k + 4 * j]), 4);
+		if (rc < 0)
+			dev_err(priv->dev,
+				"chn %d calib %d bulk_wr err = %d\n",
+				i, j, rc);
+	}
 }
 
 int tasdevice_select_tuningprm_cfg(void *context, int prm_no,
@@ -2261,9 +2280,10 @@  int tasdevice_select_tuningprm_cfg(void *context, int prm_no,
 				tas_priv->tasdevice[i].cur_conf = cfg_no;
 			}
 		}
-	} else
+	} else {
 		dev_dbg(tas_priv->dev, "%s: Unneeded loading dsp conf %d\n",
 			__func__, cfg_no);
+	}
 
 	status |= cfg_info[rca_conf_no]->active_dev;
 
@@ -2324,13 +2344,15 @@  void tasdevice_tuning_switch(void *context, int state)
 	struct tasdevice_fw *tas_fmw = tas_priv->fmw;
 	int profile_cfg_id = tas_priv->rcabin.profile_cfg_id;
 
-	if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) {
-		dev_err(tas_priv->dev, "DSP bin file not loaded\n");
+	/* Only RCA file loaded still can work without speaker protection */
+	if (!(tas_priv->fw_state == TASDEVICE_RCA_FW_OK ||
+		tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK)) {
+		dev_err(tas_priv->dev, "No firmware loaded\n");
 		return;
 	}
 
 	if (state == 0) {
-		if (tas_priv->cur_prog < tas_fmw->nr_programs) {
+		if (tas_fmw && tas_priv->cur_prog < tas_fmw->nr_programs) {
 			/*dsp mode or tuning mode*/
 			profile_cfg_id = tas_priv->rcabin.profile_cfg_id;
 			tasdevice_select_tuningprm_cfg(tas_priv,
@@ -2340,9 +2362,10 @@  void tasdevice_tuning_switch(void *context, int state)
 
 		tasdevice_select_cfg_blk(tas_priv, profile_cfg_id,
 			TASDEVICE_BIN_BLK_PRE_POWER_UP);
-	} else
+	} else {
 		tasdevice_select_cfg_blk(tas_priv, profile_cfg_id,
 			TASDEVICE_BIN_BLK_PRE_SHUTDOWN);
+	}
 }
 EXPORT_SYMBOL_NS_GPL(tasdevice_tuning_switch,
 	SND_SOC_TAS2781_FMWLIB);
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index 9350972dfefe..239f3538c558 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -13,6 +13,7 @@ 
 // Author: Kevin Lu <kevin-lu@ti.com>
 //
 
+#include <asm/unaligned.h>
 #include <linux/crc8.h>
 #include <linux/firmware.h>
 #include <linux/gpio/consumer.h>
@@ -31,6 +32,122 @@ 
 #include <sound/tlv.h>
 #include <sound/tas2781-tlv.h>
 
+static const struct bulk_reg_val tas2563_cali_start_reg[] = {
+	{
+		.reg = TAS2563_PRM_ENFF_REG,
+		.val = { 0x40, 0x00, 0x00, 0x00 },
+	},
+	{
+		.reg = TAS2563_PRM_DISTCK_REG,
+		.val = { 0x40, 0x00, 0x00, 0x00 },
+	},
+	{
+		.reg = TAS2563_PRM_TE_SCTHR_REG,
+		.val = { 0x7f, 0xff, 0xff, 0xff },
+	},
+	{
+		.reg = TAS2563_PRM_PLT_FLAG_REG,
+		.val = { 0x40, 0x00, 0x00, 0x00 },
+	},
+	{
+		.reg = TAS2563_PRM_SINEGAIN_REG,
+		.val = { 0x0a, 0x3d, 0x70, 0xa4 },
+	},
+	{
+		.reg = TAS2563_TE_TA1_REG,
+		.val = { 0x00, 0x36, 0x91, 0x5e },
+	},
+	{
+		.reg = TAS2563_TE_TA1_AT_REG,
+		.val = { 0x00, 0x36, 0x91, 0x5e },
+	},
+	{
+		.reg = TAS2563_TE_TA2_REG,
+		.val = { 0x00, 0x06, 0xd3, 0x72 },
+	},
+	{
+		.reg = TAS2563_TE_AT_REG,
+		.val = { 0x00, 0x36, 0x91, 0x5e },
+	},
+	{
+		.reg = TAS2563_TE_DT_REG,
+		.val = { 0x00, 0x36, 0x91, 0x5e },
+	},
+};
+
+static const struct bulk_reg_val tas2781_cali_start_reg[] = {
+	{
+		.reg = TAS2781_PRM_INT_MASK_REG,
+		.val = { 0xfe },
+		.val_len = 1,
+		.is_locked = false
+	},
+	{
+		.reg = TAS2781_PRM_CLK_CFG_REG,
+		.val = { 0xdd },
+		.val_len = 1,
+		.is_locked = false
+	},
+	{
+		.reg = TAS2781_PRM_RSVD_REG,
+		.val = { 0x20 },
+		.val_len = 1,
+		.is_locked = false
+	},
+	{
+		.reg = TAS2781_PRM_TEST_57_REG,
+		.val = { 0x14 },
+		.val_len = 1,
+		.is_locked = true
+	},
+	{
+		.reg = TAS2781_PRM_TEST_62_REG,
+		.val = { 0x45 },
+		.val_len = 1,
+		.is_locked = true
+	},
+	{
+		.reg = TAS2781_PRM_PVDD_UVLO_REG,
+		.val = { 0x03 },
+		.val_len = 1,
+		.is_locked = false
+	},
+	{
+		.reg = TAS2781_PRM_CHNL_0_REG,
+		.val = { 0xA8 },
+		.val_len = 1,
+		.is_locked = false
+	},
+	{
+		.reg = TAS2781_PRM_NG_CFG0_REG,
+		.val = { 0xb9 },
+		.val_len = 1,
+		.is_locked = false
+	},
+	{
+		.reg = TAS2781_PRM_IDLE_CH_DET_REG,
+		.val = { 0x92 },
+		.val_len = 1,
+		.is_locked = false
+	},
+	{
+		.reg	= TAS2781_PRM_PLT_FLAG_REG,
+		.val = { 0x40, 0x00, 0x00, 0x00 },
+		.val_len = 4,
+		.is_locked = false
+	},
+	{
+		.reg	= TAS2781_PRM_SINEGAIN_REG,
+		.val_len = 4,
+		.is_locked = false
+	},
+	{
+		.reg	= TAS2781_PRM_SINEGAIN2_REG,
+		.val_len = 4,
+		.is_locked = false
+	},
+};
+
 static const struct i2c_device_id tasdevice_id[] = {
 	{ "tas2563", TAS2563 },
 	{ "tas2781", TAS2781 },
@@ -47,6 +164,22 @@  static const struct of_device_id tasdevice_of_match[] = {
 MODULE_DEVICE_TABLE(of, tasdevice_of_match);
 #endif
 
+static const int tas2563_cali_data_reg[] = {
+	TAS2563_PRM_R0_REG,
+	TAS2563_PRM_R0_LOW_REG,
+	TAS2563_PRM_INVR0_REG,
+	TAS2563_PRM_POW_REG,
+	TAS2563_PRM_TLIMIT_REG,
+};
+
+static const int tas2781_cali_data_reg[] = {
+	TAS2781_PRM_R0_REG,
+	TAS2781_PRM_R0_LOW_REG,
+	TAS2781_PRM_INVR0_REG,
+	TAS2781_PRM_POW_REG,
+	TAS2781_PRM_TLIMIT_REG,
+};
+
 /**
  * tas2781_digital_getvol - get the volum control
  * @kcontrol: control pointer
@@ -65,8 +198,13 @@  static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol,
 	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
 	struct soc_mixer_control *mc =
 		(struct soc_mixer_control *)kcontrol->private_value;
+	int rc;
+
+	mutex_lock(&tas_priv->codec_lock);
+	rc = tasdevice_digital_getvol(tas_priv, ucontrol, mc);
+	mutex_unlock(&tas_priv->codec_lock);
 
-	return tasdevice_digital_getvol(tas_priv, ucontrol, mc);
+	return rc;
 }
 
 static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol,
@@ -76,8 +214,13 @@  static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol,
 	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
 	struct soc_mixer_control *mc =
 		(struct soc_mixer_control *)kcontrol->private_value;
+	int rc;
+
+	mutex_lock(&tas_priv->codec_lock);
+	rc = tasdevice_digital_putvol(tas_priv, ucontrol, mc);
+	mutex_unlock(&tas_priv->codec_lock);
 
-	return tasdevice_digital_putvol(tas_priv, ucontrol, mc);
+	return rc;
 }
 
 static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
@@ -87,8 +230,13 @@  static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
 	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
 	struct soc_mixer_control *mc =
 		(struct soc_mixer_control *)kcontrol->private_value;
+	int rc;
+
+	mutex_lock(&tas_priv->codec_lock);
+	rc = tasdevice_amp_getvol(tas_priv, ucontrol, mc);
+	mutex_unlock(&tas_priv->codec_lock);
 
-	return tasdevice_amp_getvol(tas_priv, ucontrol, mc);
+	return rc;
 }
 
 static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
@@ -99,11 +247,16 @@  static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
 		snd_soc_component_get_drvdata(codec);
 	struct soc_mixer_control *mc =
 		(struct soc_mixer_control *)kcontrol->private_value;
+	int rc;
+
+	mutex_lock(&tas_priv->codec_lock);
+	rc = tasdevice_amp_putvol(tas_priv, ucontrol, mc);
+	mutex_unlock(&tas_priv->codec_lock);
 
-	return tasdevice_amp_putvol(tas_priv, ucontrol, mc);
+	return rc;
 }
 
-static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol,
+static int tasdev_force_fwload_get(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_component *component =
@@ -118,7 +271,7 @@  static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol,
 	return 0;
 }
 
-static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol,
+static int tasdev_force_fwload_put(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_component *component =
@@ -127,18 +280,496 @@  static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol,
 		snd_soc_component_get_drvdata(component);
 	bool change, val = (bool)ucontrol->value.integer.value[0];
 
+	mutex_lock(&tas_priv->codec_lock);
 	if (tas_priv->force_fwload_status == val)
 		change = false;
 	else {
 		change = true;
 		tas_priv->force_fwload_status = val;
 	}
+	mutex_unlock(&tas_priv->codec_lock);
 	dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
 		tas_priv->force_fwload_status ? "ON" : "OFF");
 
 	return change;
 }
 
+static int tasdev_cali_data_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+	struct soc_bytes_ext *bytes_ext =
+		(struct soc_bytes_ext *) kcontrol->private_value;
+	unsigned char *dst = ucontrol->value.bytes.data;
+	unsigned char *data = tas_priv->cali_data.data;
+
+	mutex_lock(&tas_priv->codec_lock);
+	if (!tas_priv->is_user_space_calidata ||
+		tas_priv->cali_data.total_sz != bytes_ext->max) {
+		goto out;
+	}
+	memcpy(dst, data, bytes_ext->max);
+out:
+	mutex_unlock(&tas_priv->codec_lock);
+	return 0;
+}
+
+static int calib_data_get(struct tasdevice_priv *tas_priv, int reg,
+	unsigned char *dst, int data_len)
+{
+	struct i2c_client *clt = (struct i2c_client *)tas_priv->client;
+	struct tasdevice *tasdev = tas_priv->tasdevice;
+	int rc = -1;
+	int i;
+
+	if (data_len != 4)
+		return rc;
+
+	for (i = 0; i < tas_priv->ndev; i++) {
+		if (clt->addr == tasdev[i].dev_addr) {
+			/* First byte is the device index. */
+			dst[0] = i;
+			tasdevice_dev_bulk_read(tas_priv, i, reg, &dst[1],
+				4);
+			rc = 0;
+			break;
+		}
+	}
+
+	return rc;
+}
+
+static int tas2781_calib_start_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+	struct soc_bytes_ext *bytes_ext =
+		(struct soc_bytes_ext *) kcontrol->private_value;
+	const int sum = ARRAY_SIZE(tas2781_cali_start_reg);
+	unsigned char *dat = ucontrol->value.bytes.data;
+	int rc = 1;
+	int i, j;
+
+	mutex_lock(&tas_priv->codec_lock);
+	if (tas_priv->chip_id != TAS2781 &&
+		bytes_ext->max != 8 * tas_priv->ndev) {
+		rc = -1;
+		goto out;
+	}
+
+	for (i = 0; i < tas_priv->ndev; i++) {
+		struct tasdevice *tasdev = tas_priv->tasdevice;
+		struct bulk_reg_val *p = tasdev[i].cali_data_restore;
+		int k = i * 9;
+
+		if (p == NULL)
+			continue;
+
+		for (j = 0; j < sum; j++) {
+			if (p[j].val_len == 1) {
+				if (p[j].is_locked)
+					tasdevice_dev_write(tas_priv, i,
+						TAS2781_TEST_UNLOCK_REG,
+						TAS2781_TEST_PAGE_UNLOCK);
+				tasdevice_dev_read(tas_priv, i, p[j].reg,
+					(int *)&p[j].val[0]);
+			} else
+				tasdevice_dev_bulk_read(tas_priv, i, p[j].reg,
+					p[j].val, 4);
+		}
+
+		for (j = 0; j < sum - 2; j++) {
+			if (p[j].val_len == 1) {
+				if (p[j].is_locked)
+					tasdevice_dev_write(tas_priv, i,
+						TAS2781_TEST_UNLOCK_REG,
+						TAS2781_TEST_PAGE_UNLOCK);
+				tasdevice_dev_write(tas_priv, i, p[j].reg,
+					tas2781_cali_start_reg[j].val[0]);
+			} else
+				tasdevice_dev_bulk_write(tas_priv, i, p[j].reg,
+					(unsigned char *)
+					tas2781_cali_start_reg[j].val, 4);
+		}
+
+		if (dat[k] != i) {
+			dev_err(tas_priv->dev,
+				"%s: no cal-setting for dev %d\n", __func__,
+				i);
+			continue;
+		}
+		tasdevice_dev_bulk_write(tas_priv, i, p[j].reg,
+			&dat[i * 9 + 1], 4);
+		tasdevice_dev_bulk_write(tas_priv, i, p[j + 1].reg,
+			&dat[i * 9 + 5], 4);
+	}
+out:
+	mutex_unlock(&tas_priv->codec_lock);
+	return rc;
+}
+
+static void tas2781_calib_stop_put(struct tasdevice_priv *tas_priv)
+{
+	const int sum = ARRAY_SIZE(tas2781_cali_start_reg);
+	int i, j;
+
+	for (i = 0; i < tas_priv->ndev; i++) {
+		struct tasdevice *tasdev = tas_priv->tasdevice;
+		struct bulk_reg_val *p = tasdev[i].cali_data_restore;
+
+		if (p == NULL)
+			continue;
+
+		for (j = 0; j < sum; j++) {
+			if (p[j].val_len == 1) {
+				if (p[j].is_locked)
+					tasdevice_dev_write(tas_priv, i,
+						TAS2781_TEST_UNLOCK_REG,
+						TAS2781_TEST_PAGE_UNLOCK);
+				tasdevice_dev_write(tas_priv, i, p[j].reg,
+					p[j].val[0]);
+			} else
+				tasdevice_dev_bulk_write(tas_priv, i, p[j].reg,
+					p[j].val, 4);
+		}
+	}
+}
+
+static int tas2563_calib_start_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+	const int sum = ARRAY_SIZE(tas2563_cali_start_reg);
+	int rc = 1;
+	int i, j;
+
+	mutex_lock(&tas_priv->codec_lock);
+	if (tas_priv->chip_id != TAS2563) {
+		rc = -1;
+		goto out;
+	}
+
+	for (i = 0; i < tas_priv->ndev; i++) {
+		struct tasdevice *tasdev = tas_priv->tasdevice;
+		struct bulk_reg_val *p = tasdev[i].cali_data_restore;
+
+		if (p == NULL)
+			continue;
+		for (j = 0; j < sum; j++) {
+			tasdevice_dev_bulk_read(tas_priv, i, p[j].reg,
+				p[j].val, 4);
+		}
+
+		for (j = 0; j < sum; j++) {
+			tasdevice_dev_bulk_write(tas_priv, i, p[j].reg,
+				(unsigned char *)tas2563_cali_start_reg[j].val,
+				4);
+		}
+	}
+out:
+	mutex_unlock(&tas_priv->codec_lock);
+	return rc;
+}
+
+static void tas2563_calib_stop_put(struct tasdevice_priv *tas_priv)
+{
+	const int sum = ARRAY_SIZE(tas2563_cali_start_reg);
+	int i, j;
+
+	for (i = 0; i < tas_priv->ndev; i++) {
+		struct tasdevice *tasdev = tas_priv->tasdevice;
+		struct bulk_reg_val *p = tasdev[i].cali_data_restore;
+
+		if (p == NULL)
+			continue;
+
+		for (j = 0; j < sum; j++) {
+			tasdevice_dev_bulk_write(tas_priv, i, p[j].reg,
+				p[j].val, 4);
+		}
+	}
+}
+
+static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+
+	mutex_lock(&tas_priv->codec_lock);
+	if (tas_priv->chip_id == TAS2563)
+		tas2563_calib_stop_put(tas_priv);
+	else
+		tas2781_calib_stop_put(tas_priv);
+	mutex_unlock(&tas_priv->codec_lock);
+
+	return 1;
+}
+
+static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+	struct soc_bytes_ext *bytes_ext =
+		(struct soc_bytes_ext *) kcontrol->private_value;
+	unsigned char *data = tas_priv->cali_data.data;
+	int rc = 1;
+
+	mutex_lock(&tas_priv->codec_lock);
+	if (tas_priv->cali_data.total_sz != bytes_ext->max) {
+		rc = -1;
+		goto out;
+	}
+	tas_priv->is_user_space_calidata = true;
+	memcpy(data, ucontrol->value.bytes.data, bytes_ext->max);
+out:
+	mutex_unlock(&tas_priv->codec_lock);
+	return rc;
+}
+
+static int tas2781_latch_reg_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+	struct i2c_client *clt = (struct i2c_client *)tas_priv->client;
+	struct tasdevice *tasdev = tas_priv->tasdevice;
+	unsigned char *dst = ucontrol->value.bytes.data;
+	int i, val, rc = -1;
+
+	mutex_lock(&tas_priv->codec_lock);
+	for (i = 0; i < tas_priv->ndev; i++) {
+		if (clt->addr == tasdev[i].dev_addr) {
+			/* First byte is the device index. */
+			dst[0] = i;
+			tasdevice_dev_read(tas_priv, i,
+				TAS2781_RUNTIME_LATCH_RE_REG, &val);
+			dst[1] = val;
+			rc = 0;
+			break;
+		}
+	}
+	mutex_unlock(&tas_priv->codec_lock);
+	return rc;
+}
+
+static int tasdev_tf_data_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+	struct soc_bytes_ext *bytes_ext =
+		(struct soc_bytes_ext *) kcontrol->private_value;
+	unsigned char *dst = ucontrol->value.bytes.data;
+	unsigned int reg;
+	int rc = -1;
+
+	if (tas_priv->chip_id == TAS2781)
+		reg = TAS2781_RUNTIME_RE_REG_TF;
+	else
+		reg = TAS2563_RUNTIME_RE_REG_TF;
+
+	mutex_lock(&tas_priv->codec_lock);
+	rc = calib_data_get(tas_priv, reg, dst, bytes_ext->max - 1);
+	mutex_unlock(&tas_priv->codec_lock);
+
+	return rc;
+}
+
+static int tasdev_re_data_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+	struct soc_bytes_ext *bytes_ext =
+		(struct soc_bytes_ext *) kcontrol->private_value;
+	unsigned char *dst = ucontrol->value.bytes.data;
+	unsigned int reg;
+	int rc = -1;
+
+	if (tas_priv->chip_id == TAS2781)
+		reg = TAS2781_RUNTIME_RE_REG;
+	else
+		reg = TAS2563_RUNTIME_RE_REG;
+	mutex_lock(&tas_priv->codec_lock);
+	rc = calib_data_get(tas_priv, reg, dst, bytes_ext->max - 1);
+	mutex_unlock(&tas_priv->codec_lock);
+
+	return rc;
+}
+
+static int tasdev_r0_data_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+	struct soc_bytes_ext *bytes_ext =
+		(struct soc_bytes_ext *) kcontrol->private_value;
+	unsigned char *dst = ucontrol->value.bytes.data;
+	unsigned int reg;
+	int rc = -1;
+
+	if (tas_priv->chip_id == TAS2781)
+		reg = TAS2781_PRM_R0_REG;
+	else
+		reg = TAS2563_PRM_R0_REG;
+	mutex_lock(&tas_priv->codec_lock);
+	rc = calib_data_get(tas_priv, reg, dst, bytes_ext->max - 1);
+	mutex_unlock(&tas_priv->codec_lock);
+
+	return rc;
+}
+
+static int tasdev_XMA1_data_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+	struct soc_bytes_ext *bytes_ext =
+		(struct soc_bytes_ext *) kcontrol->private_value;
+	unsigned char *dst = ucontrol->value.bytes.data;
+	unsigned int reg = TASDEVICE_XM_A1_REG;
+	int rc = -1;
+
+	mutex_lock(&tas_priv->codec_lock);
+	rc = calib_data_get(tas_priv, reg, dst, bytes_ext->max - 1);
+	mutex_unlock(&tas_priv->codec_lock);
+
+	return rc;
+}
+
+static int tasdev_XMA2_data_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+	struct soc_bytes_ext *bytes_ext =
+		(struct soc_bytes_ext *) kcontrol->private_value;
+	unsigned char *dst = ucontrol->value.bytes.data;
+	unsigned int reg = TASDEVICE_XM_A2_REG;
+	int rc = -1;
+
+	mutex_lock(&tas_priv->codec_lock);
+	rc = calib_data_get(tas_priv, reg, dst, bytes_ext->max - 1);
+	mutex_unlock(&tas_priv->codec_lock);
+
+	return rc;
+}
+
+static int tasdev_nop_get(
+	struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	return 0;
+}
+
+static int tas2563_digital_gain_get(
+	struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+	unsigned int l = 0, r = mc->max;
+	unsigned int target, ar_mid, mid, ar_l, ar_r;
+	unsigned int reg = mc->reg;
+	unsigned char data[4];
+	int ret;
+
+	mutex_lock(&tas_dev->codec_lock);
+	/* Read the primary device */
+	ret =  tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4);
+	if (ret) {
+		dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__);
+		goto out;
+	}
+
+	target = get_unaligned_be32(&data[0]);
+
+	while (r > 1 + l) {
+		mid = (l + r) / 2;
+		ar_mid = get_unaligned_be32(tas2563_dvc_table[mid]);
+		if (target < ar_mid)
+			r = mid;
+		else
+			l = mid;
+	}
+
+	ar_l = get_unaligned_be32(tas2563_dvc_table[l]);
+	ar_r = get_unaligned_be32(tas2563_dvc_table[r]);
+
+	ucontrol->value.integer.value[0] =
+		abs(target - ar_l) <= abs(target - ar_r) ? l : r;
+out:
+	mutex_unlock(&tas_dev->codec_lock);
+	return 0;
+}
+
+static int tas2563_digital_gain_put(
+	struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+	unsigned int reg = mc->reg;
+	unsigned int volrd, volwr;
+	int vol = ucontrol->value.integer.value[0];
+	int max = mc->max, i, ret = 1;
+	unsigned char data[4];
+
+	vol = clamp(vol, 0, max);
+	mutex_lock(&tas_dev->codec_lock);
+	/* Read the primary device */
+	ret =  tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4);
+	if (ret) {
+		dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__);
+		goto out;
+	}
+
+	volrd = get_unaligned_be32(&data[0]);
+	volwr = get_unaligned_be32(tas2563_dvc_table[vol]);
+
+	if (volrd == volwr) {
+		ret = 0;
+		goto out;
+	}
+
+	for (i = 0; i < tas_dev->ndev; i++) {
+		ret = tasdevice_dev_bulk_write(tas_dev, i, reg,
+			(unsigned char *)tas2563_dvc_table[vol], 4);
+		if (ret)
+			dev_err(tas_dev->dev,
+				"%s, set digital vol error in device %d\n",
+				__func__, i);
+	}
+
+out:
+	mutex_unlock(&tas_dev->codec_lock);
+	return ret;
+}
+
+static const struct snd_kcontrol_new tasdevice_snd_controls[] = {
+	SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0,
+		tasdev_force_fwload_get, tasdev_force_fwload_put),
+};
+
+static const struct snd_kcontrol_new tasdevice_cali_controls[] = {
+	SOC_SINGLE_EXT("Calibration Stop", SND_SOC_NOPM, 0, 1, 0,
+		tasdev_nop_get, tasdev_calib_stop_put),
+	SND_SOC_BYTES_EXT("Amp TF Data", 5, tasdev_tf_data_get, NULL),
+	SND_SOC_BYTES_EXT("Amp RE Data", 5, tasdev_re_data_get, NULL),
+	SND_SOC_BYTES_EXT("Amp R0 Data", 5, tasdev_r0_data_get, NULL),
+	SND_SOC_BYTES_EXT("Amp XMA1 Data", 5, tasdev_XMA1_data_get, NULL),
+	SND_SOC_BYTES_EXT("Amp XMA2 Data", 5, tasdev_XMA2_data_get, NULL),
+};
+
 static const struct snd_kcontrol_new tas2781_snd_controls[] = {
 	SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL,
 		1, 0, 20, 0, tas2781_amp_getvol,
@@ -146,8 +777,22 @@  static const struct snd_kcontrol_new tas2781_snd_controls[] = {
 	SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL,
 		0, 0, 200, 1, tas2781_digital_getvol,
 		tas2781_digital_putvol, dvc_tlv),
-	SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0,
-		tas2781_force_fwload_get, tas2781_force_fwload_put),
+};
+
+static const struct snd_kcontrol_new tas2781_cali_controls[] = {
+	SND_SOC_BYTES_EXT("Amp Latch Data", 2, tas2781_latch_reg_get, NULL),
+};
+
+static const struct snd_kcontrol_new tas2563_snd_controls[] = {
+	SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2563_DVC_LVL, 0,
+		0, ARRAY_SIZE(tas2563_dvc_table) - 1, 0,
+		tas2563_digital_gain_get, tas2563_digital_gain_put,
+		tas2563_dvc_tlv),
+};
+
+static const struct snd_kcontrol_new tas2563_cali_controls[] = {
+	SOC_SINGLE_EXT("Calibration Start", SND_SOC_NOPM, 0, 1, 0,
+		tasdev_nop_get, tas2563_calib_start_put),
 };
 
 static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
@@ -167,6 +812,31 @@  static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
 	return ret;
 }
 
+static int tasdevice_info_active_id(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = (int)tas_priv->ndev - 1;
+
+	return 0;
+}
+
+static int tasdevice_info_chip_id(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = TAS2563;
+	uinfo->value.integer.max = TAS2781;
+
+	return 0;
+}
+
 static int tasdevice_info_programs(struct snd_kcontrol *kcontrol,
 			struct snd_ctl_elem_info *uinfo)
 {
@@ -223,6 +893,17 @@  static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol,
 	return 0;
 }
 
+static int tasdevice_get_chip_id(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tas_priv->chip_id;
+
+	return 0;
+}
+
 static int tasdevice_create_control(struct tasdevice_priv *tas_priv)
 {
 	struct snd_kcontrol_new *prof_ctrls;
@@ -316,15 +997,54 @@  static int tasdevice_configuration_put(
 	return ret;
 }
 
-static int tasdevice_dsp_create_ctrls(
-	struct tasdevice_priv *tas_priv)
+static int tasdevice_active_id_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+	struct i2c_client *clt = (struct i2c_client *)tas_priv->client;
+	struct tasdevice *tasdev = tas_priv->tasdevice;
+	int i;
+
+	for (i = 0; i < tas_priv->ndev; i++) {
+		if (clt->addr == tasdev[i].dev_addr) {
+			ucontrol->value.integer.value[0] = i;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+static int tasdevice_active_id_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+	int nr_dev = ucontrol->value.integer.value[0];
+	int max = tas_priv->ndev - 1, rc;
+
+	nr_dev = clamp(nr_dev, 0, max);
+
+	mutex_lock(&tas_priv->codec_lock);
+	rc = tasdevice_chn_switch(tas_priv, nr_dev);
+	mutex_unlock(&tas_priv->codec_lock);
+
+	return rc;
+}
+
+static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv)
 {
+	char *active_dev_name, *cali_name, *chip_id, *conf_name, *prog_name;
+	struct calidata *cali_data = &tas_priv->cali_data;
 	struct snd_kcontrol_new *dsp_ctrls;
-	char *prog_name, *conf_name;
-	int nr_controls = 2;
+	struct soc_bytes_ext *ext_cali_data;
+	int nr_controls = 5;
 	int mix_index = 0;
 	int ret;
 
+	if (tas_priv->chip_id == TAS2781)
+		nr_controls++;
 	/* Alloc kcontrol via devm_kzalloc, which don't manually
 	 * free the kcontrol
 	 */
@@ -336,11 +1056,20 @@  static int tasdevice_dsp_create_ctrls(
 	}
 
 	/* Create a mixer item for selecting the active profile */
-	prog_name = devm_kzalloc(tas_priv->dev,
+	active_dev_name = devm_kzalloc(tas_priv->dev,
+		SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL);
+	cali_name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+		GFP_KERNEL);
+	chip_id = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+		GFP_KERNEL);
+	conf_name = devm_kzalloc(tas_priv->dev,
 		SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL);
-	conf_name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+	prog_name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
 		GFP_KERNEL);
-	if (!prog_name || !conf_name) {
+	ext_cali_data = devm_kzalloc(tas_priv->dev, sizeof(*ext_cali_data),
+		GFP_KERNEL);
+	if (!active_dev_name || !cali_name || !conf_name || !chip_id ||
+		!ext_cali_data || !prog_name) {
 		ret = -ENOMEM;
 		goto out;
 	}
@@ -363,6 +1092,68 @@  static int tasdevice_dsp_create_ctrls(
 	dsp_ctrls[mix_index].put = tasdevice_configuration_put;
 	mix_index++;
 
+	scnprintf(active_dev_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+		"Activate Tasdevice Id");
+	dsp_ctrls[mix_index].name = active_dev_name;
+	dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	dsp_ctrls[mix_index].info = tasdevice_info_active_id;
+	dsp_ctrls[mix_index].get = tasdevice_active_id_get;
+	dsp_ctrls[mix_index].put = tasdevice_active_id_put;
+	mix_index++;
+
+	scnprintf(chip_id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+		"Tasdevice Chip Id");
+	dsp_ctrls[mix_index].name = chip_id;
+	dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	dsp_ctrls[mix_index].info = tasdevice_info_chip_id;
+	dsp_ctrls[mix_index].get = tasdevice_get_chip_id;
+	mix_index++;
+
+	scnprintf(cali_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+		"Speaker Calibrated Data");
+	ext_cali_data->max = tas_priv->ndev * CAL_DAT_SZ;
+	tas_priv->cali_data.total_sz = ext_cali_data->max;
+	tas_priv->cali_data.data = devm_kzalloc(tas_priv->dev,
+		ext_cali_data->max, GFP_KERNEL);
+	dsp_ctrls[mix_index].name = cali_name;
+	dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	dsp_ctrls[mix_index].info = snd_soc_bytes_info_ext;
+	dsp_ctrls[mix_index].get = tasdev_cali_data_get;
+	dsp_ctrls[mix_index].put = tasdev_cali_data_put;
+	dsp_ctrls[mix_index].private_value = (unsigned long)ext_cali_data;
+	mix_index++;
+
+	cali_data->data = devm_kzalloc(tas_priv->dev, tas_priv->ndev *
+		(cali_data->reg_array_sz * 4 + 1), GFP_KERNEL);
+	if (!cali_data->data)
+		return -ENOMEM;
+
+	if (tas_priv->chip_id == TAS2781) {
+		struct soc_bytes_ext *ext_cali_start;
+		char *cali_start_name;
+
+		cali_start_name = devm_kzalloc(tas_priv->dev,
+			SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL);
+		ext_cali_start = devm_kzalloc(tas_priv->dev,
+			sizeof(*ext_cali_start), GFP_KERNEL);
+		if (!cali_start_name || !ext_cali_start) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		scnprintf(cali_start_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+			"Calibration Start");
+		ext_cali_data->max = tas_priv->ndev * 9;
+		dsp_ctrls[mix_index].name = cali_start_name;
+		dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		dsp_ctrls[mix_index].info = snd_soc_bytes_info_ext;
+		dsp_ctrls[mix_index].put = tas2781_calib_start_put;
+		dsp_ctrls[mix_index].get = tasdev_nop_get;
+		dsp_ctrls[mix_index].private_value =
+			(unsigned long)ext_cali_start;
+		mix_index++;
+	}
+
 	ret = snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls,
 		nr_controls < mix_index ? nr_controls : mix_index);
 
@@ -370,6 +1161,54 @@  static int tasdevice_dsp_create_ctrls(
 	return ret;
 }
 
+static int tasdevice_create_cali_ctrls(struct tasdevice_priv *tas_priv)
+{
+	struct calidata *cali_data = &tas_priv->cali_data;
+	struct snd_kcontrol_new *cali_ctrls;
+	unsigned int num_controls;
+	int rc;
+
+	rc = snd_soc_add_component_controls(tas_priv->codec,
+		tasdevice_cali_controls, ARRAY_SIZE(tasdevice_cali_controls));
+	if (rc < 0) {
+		dev_err(tas_priv->dev, "%s: Add cali control err rc = %d",
+			__func__, rc);
+		return rc;
+	}
+
+	if (tas_priv->chip_id == TAS2781) {
+		cali_ctrls = (struct snd_kcontrol_new *)tas2781_cali_controls;
+		num_controls = ARRAY_SIZE(tas2781_cali_controls);
+		cali_data->reg_array = (unsigned int *)tas2781_cali_data_reg;
+		cali_data->reg_array_sz = ARRAY_SIZE(tas2781_cali_data_reg);
+	} else {
+		struct tasdevice *tasdev = tas_priv->tasdevice;
+		int i;
+
+		cali_ctrls = (struct snd_kcontrol_new *)tas2563_cali_controls;
+		num_controls = ARRAY_SIZE(tas2563_cali_controls);
+		cali_data->reg_array = (unsigned int *)tas2563_cali_data_reg;
+		cali_data->reg_array_sz = sizeof(tas2563_cali_data_reg);
+		for (i = 0; i < tas_priv->ndev; i++) {
+			tasdev[i].cali_data_restore =
+				kmemdup(tas2563_cali_start_reg,
+				sizeof(tas2563_cali_start_reg), GFP_KERNEL);
+			if (!tasdev[i].cali_data_restore)
+				return -ENOMEM;
+		}
+	}
+
+	rc = snd_soc_add_component_controls(tas_priv->codec, cali_ctrls,
+		num_controls);
+	if (rc < 0) {
+		dev_err(tas_priv->dev, "%s: Add control err rc = %d",
+			__func__, rc);
+		return rc;
+	}
+
+	return rc;
+}
+
 static void tasdevice_fw_ready(const struct firmware *fmw,
 	void *context)
 {
@@ -380,23 +1219,36 @@  static void tasdevice_fw_ready(const struct firmware *fmw,
 	mutex_lock(&tas_priv->codec_lock);
 
 	ret = tasdevice_rca_parser(tas_priv, fmw);
-	if (ret)
+	if (ret) {
+		tasdevice_config_info_remove(tas_priv);
 		goto out;
+	}
 	tasdevice_create_control(tas_priv);
 
 	tasdevice_dsp_remove(tas_priv);
 	tasdevice_calbin_remove(tas_priv);
-	tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+	tas_priv->fw_state = TASDEVICE_RCA_FW_OK;
 	scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin",
 		tas_priv->dev_name);
+
 	ret = tasdevice_dsp_parser(tas_priv);
 	if (ret) {
 		dev_err(tas_priv->dev, "dspfw load %s error\n",
 			tas_priv->coef_binaryname);
-		tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
 		goto out;
 	}
-	tasdevice_dsp_create_ctrls(tas_priv);
+
+	ret = tasdevice_dsp_create_ctrls(tas_priv);
+	if (ret) {
+		dev_err(tas_priv->dev, "dsp controls error\n");
+		goto out;
+	}
+
+	ret = tasdevice_create_cali_ctrls(tas_priv);
+	if (ret) {
+		dev_err(tas_priv->dev, "cali controls error\n");
+		goto out;
+	}
 
 	tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
 
@@ -417,9 +1269,8 @@  static void tasdevice_fw_ready(const struct firmware *fmw,
 	tasdevice_prmg_load(tas_priv, 0);
 	tas_priv->cur_prog = 0;
 out:
-	if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) {
-		/*If DSP FW fail, kcontrol won't be created */
-		tasdevice_config_info_remove(tas_priv);
+	if (tas_priv->fw_state == TASDEVICE_RCA_FW_OK) {
+		/*If DSP FW fail, DSP kcontrol won't be created */
 		tasdevice_dsp_remove(tas_priv);
 	}
 	mutex_unlock(&tas_priv->codec_lock);
@@ -466,14 +1317,14 @@  static int tasdevice_startup(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_component *codec = dai->component;
 	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
-	int ret = 0;
 
-	if (tas_priv->fw_state != TASDEVICE_DSP_FW_ALL_OK) {
-		dev_err(tas_priv->dev, "DSP bin file not loaded\n");
-		ret = -EINVAL;
+	if (!(tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK ||
+		tas_priv->fw_state == TASDEVICE_RCA_FW_OK)) {
+		dev_err(tas_priv->dev, "Bin file not loaded\n");
+		return -EINVAL;
 	}
 
-	return ret;
+	return 0;
 }
 
 static int tasdevice_hw_params(struct snd_pcm_substream *substream,
@@ -541,7 +1392,7 @@  static const struct snd_soc_dai_ops tasdevice_dai_ops = {
 
 static struct snd_soc_dai_driver tasdevice_dai_driver[] = {
 	{
-		.name = "tas2781_codec",
+		.name = "tasdev_codec",
 		.id = 0,
 		.playback = {
 			.stream_name = "Playback",
@@ -565,13 +1416,41 @@  static struct snd_soc_dai_driver tasdevice_dai_driver[] = {
 static int tasdevice_codec_probe(struct snd_soc_component *codec)
 {
 	struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec);
+	int rc;
+
+	if (tas_priv->chip_id == TAS2781) {
+		rc = snd_soc_add_component_controls(codec,
+			tas2781_snd_controls,
+			ARRAY_SIZE(tas2781_snd_controls));
+		if (rc < 0) {
+			dev_err(tas_priv->dev, "%s: Add control err rc = %d",
+				__func__, rc);
+			return rc;
+		}
+	} else {
+		rc = snd_soc_add_component_controls(codec,
+			tas2563_snd_controls,
+			ARRAY_SIZE(tas2563_snd_controls));
+		if (rc < 0) {
+			dev_err(tas_priv->dev, "%s: Add control err rc = %d",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	rc = tascodec_init(tas_priv, codec, THIS_MODULE, tasdevice_fw_ready);
 
-	return tascodec_init(tas_priv, codec, THIS_MODULE, tasdevice_fw_ready);
+	return rc;
 }
 
 static void tasdevice_deinit(void *context)
 {
 	struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+	struct tasdevice *tasdev = tas_priv->tasdevice;
+	int i;
+
+	for (i = 0; i < tas_priv->ndev; i++)
+		kfree(tasdev[i].cali_data_restore);
 
 	tasdevice_config_info_remove(tas_priv);
 	tasdevice_dsp_remove(tas_priv);
@@ -591,8 +1470,8 @@  static const struct snd_soc_component_driver
 	soc_codec_driver_tasdevice = {
 	.probe			= tasdevice_codec_probe,
 	.remove			= tasdevice_codec_remove,
-	.controls		= tas2781_snd_controls,
-	.num_controls		= ARRAY_SIZE(tas2781_snd_controls),
+	.controls		= tasdevice_snd_controls,
+	.num_controls		= ARRAY_SIZE(tasdevice_snd_controls),
 	.dapm_widgets		= tasdevice_dapm_widgets,
 	.num_dapm_widgets	= ARRAY_SIZE(tasdevice_dapm_widgets),
 	.dapm_routes		= tasdevice_audio_map,