From: Shenghao Ding <shenghao-ding@ti.com>
To: <broonie@kernel.org>
Cc: <andriy.shevchenko@linux.intel.com>, <tiwai@suse.de>,
<13916275206@139.com>, <krzk@kernel.org>,
<alsa-devel@alsa-project.org>, <baojun.xu@ti.com>, <riyo@ti.com>,
Shenghao Ding <shenghao-ding@ti.com>
Subject: [PATCH v1] ASoC: tas2781: Add calibration kcontrols for TAS2573
Date: Fri, 17 Jul 2026 15:39:27 +0800 [thread overview]
Message-ID: <20260717073927.181-1-shenghao-ding@ti.com> (raw)
Add calibration kcontrols for TAS2573 in TAS2781 driver to support
device-specific calibration settings.
Signed-off-by: Shenghao Ding <shenghao-ding@ti.com>
---
include/sound/tas2781-dsp.h | 2 +
include/sound/tas2781.h | 25 +++
sound/soc/codecs/tas2781-fmwlib.c | 28 ++-
sound/soc/codecs/tas2781-i2c.c | 290 +++++++++++++++++++++++++-----
4 files changed, 295 insertions(+), 50 deletions(-)
diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h
index dd6ee45ad096..5f3197f83a34 100644
--- a/include/sound/tas2781-dsp.h
+++ b/include/sound/tas2781-dsp.h
@@ -142,6 +142,7 @@ struct tasdevice_fw {
struct tasdevice_calibration *calibrations;
struct fct_param_address fct_par_addr;
struct device *dev;
+ int calibration_config_id;
};
enum tasdevice_fw_state {
@@ -201,6 +202,7 @@ struct tasdevice_rca {
int ncfgs;
struct tasdevice_config_info **cfg_info;
int profile_cfg_id;
+ int calibration_profile_id;
/*
* Since version 0x105, the keyword 'init' was introduced into the
* profile, which is used for chip initialization, particularly to
diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h
index b763da613769..d9fa3e02bfd5 100644
--- a/include/sound/tas2781.h
+++ b/include/sound/tas2781.h
@@ -17,6 +17,8 @@
#ifndef __TAS2781_H__
#define __TAS2781_H__
+#define CONFIG_SND_SOC_TAS2781_ACOUST_I2C
+
#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
#include <linux/debugfs.h>
#endif
@@ -91,6 +93,29 @@
/* prm_TE_1_Beta1 */
#define TAS2563_TE_DT_REG TASDEVICE_REG(0x00, 0x0f, 0x70)
+#define TAS2573_FCT_INT_LATCH TASDEVICE_REG(0x00, 0x00, 0x67)
+// YM147
+#define TAS2573_FCT_STATUS_CTRL TASDEVICE_REG(0x00, 0x68, 0x74)
+// YM148
+#define TAS2573_FCT_STATUS_BINNING TASDEVICE_REG(0x00, 0x68, 0x78)
+// YM57
+#define TAS2573_FCT_OUTPUT_R0 TASDEVICE_REG(0x00, 0x65, 0x74)
+// YM58
+#define TAS2573_FCT_OUTPUT_R0_LOW TASDEVICE_REG(0x00, 0x65, 0x78)
+// YM59
+#define TAS2573_FCT_OUTPUT_INV_R0 TASDEVICE_REG(0x00, 0x65, 0x7C)
+// YM61
+#define TAS2573_FCT_OUTPUT_POWERTOT TASDEVICE_REG(0x00, 0x66, 0x0C)
+// YM64
+#define TAS2573_FCT_OUTPUT_F0 TASDEVICE_REG(0x00, 0x65, 0x18)
+// YM952
+#define TAS2573_SILENCE_DETECTED TASDEVICE_REG(0x00, 0x83, 0x60)
+// YM954
+#define TAS2573_OPEN_CIRCUIT TASDEVICE_REG(0x00, 0x83, 0x68)
+// YM955
+#define TAS2573_SHORTCKT TASDEVICE_REG(0x00, 0x83, 0x6C)
+
+
#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)
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
index bd16d5326a23..95cfcd72dfe5 100644
--- a/sound/soc/codecs/tas2781-fmwlib.c
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -199,14 +199,20 @@ static struct tasdevice_config_info *tasdevice_add_config(
goto out;
}
/* If in the RCA bin file are several profiles with the
- * keyword "init", init_profile_id only store the last
- * init profile id.
+ * keyword "init"/"calib", init_profile_id only store the last
+ * init profile id, and same as calibration_profile_id.
*/
if (strnstr(&config_data[config_offset], "init", 64)) {
tas_priv->rcabin.init_profile_id =
tas_priv->rcabin.ncfgs - 1;
dev_dbg(tas_priv->dev, "%s: init profile id = %d\n",
__func__, tas_priv->rcabin.init_profile_id);
+ } else if (strnstr(&config_data[config_offset], "calib", 64)) {
+ tas_priv->rcabin.calibration_profile_id =
+ tas_priv->rcabin.ncfgs - 1;
+ dev_dbg(tas_priv->dev, "%s: calib profile id = %d\n",
+ __func__,
+ tas_priv->rcabin.calibration_profile_id);
}
config_offset += 64;
}
@@ -631,6 +637,15 @@ static int fw_parse_configuration_data_kernel(
goto out;
}
memcpy(config->name, &data[offset], 64);
+ /* If in the coef bin file are several configs with the
+ * keyword "calib", init_profile_id only store the last
+ * init profile id, and same as calibration_profile_id.
+ */
+ if (strnstr(config->name, "calib", 64)) {
+ tas_fmw->calibration_config_id = i;
+ dev_dbg(tas_priv->dev, "%s: calib cofig id = %d\n",
+ __func__, tas_fmw->calibration_config_id);
+ }
/*skip extra 16 bytes*/
offset += 80;
@@ -1371,6 +1386,15 @@ static int fw_parse_configuration_data(
}
memcpy(config->name, &data[offset], 64);
offset += 64;
+ /* If in the coef bin file are several configs with the
+ * keyword "calib", init_profile_id only store the last
+ * init profile id, and same as calibration_profile_id.
+ */
+ if (strnstr(config->name, "calib", 64)) {
+ tas_fmw->calibration_config_id = i;
+ dev_dbg(tas_priv->dev, "%s: calib cofig id = %d\n",
+ __func__, tas_fmw->calibration_config_id);
+ }
n = strlen((char *)&data[offset]);
n += 15;
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index 9e6f0ad5f05d..88a675117485 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -612,17 +612,68 @@ static void tas2563_calib_stop_put(struct tasdevice_priv *tas_priv)
}
}
+static void tas2573_calib_stop_put(struct tasdevice_priv *tas_priv)
+{
+ // const int sum = ARRAY_SIZE(tas2563_cali_start_reg);
+ int cal_prof_id = tas_priv->rcabin.calibration_profile_id;
+
+ tasdevice_select_cfg_blk(tas_priv, cal_prof_id,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN);
+}
+
+static int tas2573_calib_start_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ int cal_prof_id = tas_priv->rcabin.calibration_profile_id;
+ int cal_conf_id = tas_priv->fmw->calibration_config_id;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ if (tas_priv->chip_id != TAS2573)
+ return -1;
+
+ tasdevice_select_tuningprm_cfg(tas_priv, tas_priv->cur_prog,
+ cal_conf_id, cal_prof_id);
+
+ tasdevice_select_cfg_blk(tas_priv, cal_prof_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+
+ return 1;
+}
+
+
static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+ int i;
guard(mutex)(&priv->codec_lock);
- if (priv->chip_id == TAS2563)
+
+ switch (priv->chip_id) {
+ case TAS2563:
tas2563_calib_stop_put(priv);
- else
+ break;
+ case TAS2573:
+ tas2573_calib_stop_put(priv);
+ break;
+ case TAS2781:
tas2781_calib_stop_put(priv);
+ break;
+ default:
+ dev_err(priv->dev, "%s: Chip(%d) unsupports calibration\n",
+ __func__, priv->chip_id);
+ return -1;
+ }
+
+ /*
+ * Set reloading-firmware flag after calibration, the flag will work
+ * during next playback, then set to the program id after reloading.
+ */
+ for (i = 0; i < priv->ndev; i++)
+ priv->tasdevice[i].cur_prog = -1;
return 1;
}
@@ -978,6 +1029,11 @@ static const struct snd_kcontrol_new tas2563_cali_controls[] = {
tasdev_nop_get, tas2563_calib_start_put),
};
+static const struct snd_kcontrol_new tas2573_cali_controls[] = {
+ SOC_SINGLE_EXT("Calibration Start", SND_SOC_NOPM, 0, 1, 0,
+ tasdev_nop_get, tas2573_calib_start_put),
+};
+
static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1330,6 +1386,145 @@ static void alpa_cali_update(struct bulk_reg_val *p,
p->val_len = 4;
}
+static int create_tas2781_cali_start_ktrl(struct tasdevice_priv
+ *priv, struct snd_kcontrol_new *cali_ctrl)
+{
+ struct soc_bytes_ext *ext_cali_start;
+ char *cali_start_name;
+
+ ext_cali_start = devm_kzalloc(priv->dev,
+ sizeof(*ext_cali_start), GFP_KERNEL);
+ if (!ext_cali_start)
+ return -ENOMEM;
+
+ cali_start_name = devm_kstrdup(priv->dev,
+ "Calibration Start", GFP_KERNEL);
+ if (!cali_start_name)
+ return -ENOMEM;
+ /*
+ * package structure for tas2781 ftc start:
+ * Pkg len (1 byte)
+ * Reg id (1 byte, constant 'r')
+ * book, page, register for pilot threshold, pilot tone
+ * and sine gain (12 bytes)
+ * for (i = 0; i < Device-Sum; i++) {
+ * Device #i index_info (1 byte)
+ * Sine gain for Device #i (8 bytes)
+ * }
+ */
+ ext_cali_start->max = 14 + priv->ndev * 9;
+ cali_ctrl->name = cali_start_name;
+ cali_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ cali_ctrl->info = snd_soc_bytes_info_ext;
+ cali_ctrl->put = tas2781_calib_start_put;
+ cali_ctrl->get = tasdev_nop_get;
+ cali_ctrl->private_value = (unsigned long)ext_cali_start;
+
+ return 0;
+}
+
+static int tas2573_calib_status_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+ unsigned char *dst = ucontrol->value.bytes.data;
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned int fct_status_regs[] = {
+ TAS2573_FCT_INT_LATCH,
+ TAS2573_FCT_STATUS_CTRL,
+ TAS2573_FCT_STATUS_BINNING,
+ TAS2573_SILENCE_DETECTED,
+ TAS2573_OPEN_CIRCUIT,
+ TAS2573_SHORTCKT,
+ TAS2573_FCT_OUTPUT_R0,
+ TAS2573_FCT_OUTPUT_R0_LOW,
+ TAS2573_FCT_OUTPUT_INV_R0,
+ TAS2573_FCT_OUTPUT_POWERTOT,
+ TAS2573_FCT_OUTPUT_F0,
+ };
+ unsigned int j, k, val;
+ unsigned int i = 0;
+ int rc;
+
+ guard(mutex)(&priv->codec_lock);
+ dst[i++] = bytes_ext->max;
+ dst[i++] = 'r';
+
+ for (j = 0; j < ARRAY_SIZE(fct_status_regs); j++) {
+ dst[i++] = TASDEVICE_BOOK_ID(fct_status_regs[j]);
+ dst[i++] = TASDEVICE_PAGE_ID(fct_status_regs[j]);
+ dst[i++] = TASDEVICE_PAGE_REG(fct_status_regs[j]);
+ }
+ dst[i++] = 'D';
+
+ for (j = 0; j < priv->ndev; j++) {
+ dst[i++] = j;
+ dst[i++] = 0;
+ dst[i++] = 0;
+ dst[i++] = 0;
+
+ rc = tasdevice_dev_read(priv, j, fct_status_regs[0], &val);
+ if (rc < 0)
+ dev_err(priv->dev,
+ "chn %d fct_status_regs[0] rd err = %d\n",
+ j, rc);
+ else
+ dst[i++] = val;
+
+ for (k = 1; k < ARRAY_SIZE(fct_status_regs); k++, i += 4) {
+ rc = tasdevice_dev_bulk_read(priv, j,
+ fct_status_regs[k], &dst[i], 4);
+ if (rc < 0) {
+ dev_err(priv->dev,
+ "chn %d regs[%u] bulk_rd err = %d\n",
+ j, k, rc);
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+static int create_tas2573_cali_status_ktrl(struct tasdevice_priv
+ *priv, struct snd_kcontrol_new *cali_ctrl)
+{
+ struct soc_bytes_ext *ext_cali_start;
+ char *cali_start_name;
+
+ ext_cali_start = devm_kzalloc(priv->dev,
+ sizeof(*ext_cali_start), GFP_KERNEL);
+ if (!ext_cali_start)
+ return -ENOMEM;
+
+ cali_start_name = devm_kstrdup(priv->dev,
+ "Calibration Status", GFP_KERNEL);
+ if (!cali_start_name)
+ return -ENOMEM;
+ /*
+ * package structure for tas2573 fct status:
+ * Pkg len (1 byte)
+ * Reg id (1 byte, constant 'r')
+ * book, page, register for fct status (total 33 bytes)
+ * Data Start Flag (1 byte, constant 'D')
+ * for (i = 0; i < Device-Sum; i++) {
+ * Device #i index_info (1 byte)
+ * Sine gain for Device #i (44 bytes)
+ * }
+ */
+ ext_cali_start->max = 36 + priv->ndev * 45;
+ cali_ctrl->name = cali_start_name;
+ cali_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ cali_ctrl->info = snd_soc_bytes_info_ext;
+ cali_ctrl->put = NULL;
+ cali_ctrl->get = tas2573_calib_status_get;
+ cali_ctrl->private_value = (unsigned long)ext_cali_start;
+
+ return 0;
+}
+
static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
{
struct calidata *cali_data = &priv->cali_data;
@@ -1349,7 +1544,24 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
return rc;
}
- if (priv->chip_id == TAS2781) {
+ switch (priv->chip_id) {
+ case TAS2563: {
+ cali_ctrls = (struct snd_kcontrol_new *)tas2563_cali_controls;
+ nctrls = ARRAY_SIZE(tas2563_cali_controls);
+ for (i = 0; i < priv->ndev; i++) {
+ tasdev[i].cali_data_backup =
+ kmemdup(tas2563_cali_start_reg,
+ sizeof(tas2563_cali_start_reg), GFP_KERNEL);
+ if (!tasdev[i].cali_data_backup)
+ return -ENOMEM;
+ }
+ }
+ break;
+ case TAS2573:
+ cali_ctrls = (struct snd_kcontrol_new *)tas2573_cali_controls;
+ nctrls = ARRAY_SIZE(tas2573_cali_controls);
+ break;
+ case TAS2781: {
struct fct_param_address *t = &(fmw->fct_par_addr);
cali_ctrls = (struct snd_kcontrol_new *)tas2781_cali_controls;
@@ -1370,31 +1582,32 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
}
}
}
- } else {
- cali_ctrls = (struct snd_kcontrol_new *)tas2563_cali_controls;
- nctrls = ARRAY_SIZE(tas2563_cali_controls);
- for (i = 0; i < priv->ndev; i++) {
- tasdev[i].cali_data_backup =
- kmemdup(tas2563_cali_start_reg,
- sizeof(tas2563_cali_start_reg), GFP_KERNEL);
- if (!tasdev[i].cali_data_backup)
- return -ENOMEM;
- }
+ }
+ break;
+ default:
+ dev_err(priv->dev, "%s: Wrong chip id = %d", __func__,
+ priv->chip_id);
+ return -EINVAL;
}
rc = snd_soc_add_component_controls(priv->codec, cali_ctrls, nctrls);
if (rc < 0) {
dev_err(priv->dev, "%s: Add chip cali ctrls err rc = %d",
- __func__, rc);
+ __func__, priv->chip_id);
return rc;
}
/* index for cali_ctrls */
i = 0;
- if (priv->chip_id == TAS2781)
+ switch (priv->chip_id) {
+ case TAS2573:
+ case TAS2781:
nctrls = 2;
- else
+ break;
+ default:
nctrls = 1;
+ break;
+ }
/*
* Alloc kcontrol via devm_kzalloc(), which don't manually
@@ -1447,40 +1660,21 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
* it, for the default value is 0, which means the first device.
*/
cali_data->data[0] = 0xff;
- if (priv->chip_id == TAS2781) {
- struct soc_bytes_ext *ext_cali_start;
- char *cali_start_name;
-
- ext_cali_start = devm_kzalloc(priv->dev,
- sizeof(*ext_cali_start), GFP_KERNEL);
- if (!ext_cali_start)
- return -ENOMEM;
-
- cali_start_name = devm_kstrdup(priv->dev,
- "Calibration Start", GFP_KERNEL);
- if (!cali_start_name)
- return -ENOMEM;
- /*
- * package structure for tas2781 ftc start:
- * Pkg len (1 byte)
- * Reg id (1 byte, constant 'r')
- * book, page, register for pilot threshold, pilot tone
- * and sine gain (12 bytes)
- * for (i = 0; i < Device-Sum; i++) {
- * Device #i index_info (1 byte)
- * Sine gain for Device #i (8 bytes)
- * }
- */
- ext_cali_start->max = 14 + priv->ndev * 9;
- cali_ctrls[i].name = cali_start_name;
- cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- cali_ctrls[i].info = snd_soc_bytes_info_ext;
- cali_ctrls[i].put = tas2781_calib_start_put;
- cali_ctrls[i].get = tasdev_nop_get;
- cali_ctrls[i].private_value = (unsigned long)ext_cali_start;
+
+ switch (priv->chip_id) {
+ case TAS2573:
+ rc = create_tas2573_cali_status_ktrl(priv, &cali_ctrls[i]);
+ if (rc != 0)
+ return rc;
+ i++;
+ break;
+ case TAS2781:
+ rc = create_tas2781_cali_start_ktrl(priv, &cali_ctrls[i]);
+ if (rc != 0)
+ return rc;
i++;
+ break;
}
-
return snd_soc_add_component_controls(priv->codec, cali_ctrls,
nctrls < i ? nctrls : i);
}
--
2.43.0
next reply other threads:[~2026-07-17 7:40 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-17 7:39 Shenghao Ding [this message]
2026-07-17 11:02 ` [PATCH v1] ASoC: tas2781: Add calibration kcontrols for TAS2573 Andy Shevchenko
2026-08-05 22:44 ` kernel test robot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260717073927.181-1-shenghao-ding@ti.com \
--to=shenghao-ding@ti.com \
--cc=13916275206@139.com \
--cc=alsa-devel@alsa-project.org \
--cc=andriy.shevchenko@linux.intel.com \
--cc=baojun.xu@ti.com \
--cc=broonie@kernel.org \
--cc=krzk@kernel.org \
--cc=riyo@ti.com \
--cc=tiwai@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.