[PATCH v1] ASoC: tas2781: Add calibration kcontrols for TAS2573
Shenghao Ding <[email protected]> Fri, 17 Jul 2026 15:39:27 +0800
| Newsgroups | gmane.linux.alsa.devel |
|---|---|
| Message-ID | <[email protected]> |
Add calibration kcontrols for TAS2573 in TAS2781 driver to support device-specific calibration settings. Signed-off-by: Shenghao Ding <[email protected]> --- 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