[PATCH v2] ALSA: hda: Add TAS2770 support

Add TAS2770 support in TI's HDA driver. And add hda_chip_id for more products. Distinguish DSP and non-DSP in firmware loading function.
Signed-off-by: Baojun Xu baojun.xu@ti.com
--- v2: - Rewrite the patch description - Move registers define for TAS2770 from tas2781.h to tas2770-tlv.h - Add new chip ID define for HDA - Add digital volume control in tas2770_snd_controls - Change public kcontrol name from tas27xx_ to tasdevice_ - Create new function for kcontrol adding - Create new function for DSP binary loading - Rename chip_id to hda_chip_id in i2c_probe for the supported devices --- include/sound/tas2770-tlv.h | 23 +++ .../hda/codecs/side-codecs/tas2781_hda_i2c.c | 161 +++++++++++------- 2 files changed, 127 insertions(+), 57 deletions(-) create mode 100644 include/sound/tas2770-tlv.h
diff --git a/include/sound/tas2770-tlv.h b/include/sound/tas2770-tlv.h new file mode 100644 index 000000000000..c0bd495b4a07 --- /dev/null +++ b/include/sound/tas2770-tlv.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// +// ALSA SoC Texas Instruments TAS2770 Audio Smart Amplifier +// +// Copyright (C) 2025 Texas Instruments Incorporated +// https://www.ti.com +// +// The TAS2770 hda driver implements for one, two, or even multiple +// TAS2770 chips. +// +// Author: Baojun Xu baojun.xu@ti.com +// + +#ifndef __TAS2770_TLV_H__ +#define __TAS2770_TLV_H__ + +#define TAS2770_DVC_LEVEL TASDEVICE_REG(0x0, 0x0, 0x17) +#define TAS2770_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03) + +static const __maybe_unused DECLARE_TLV_DB_SCALE(tas2770_dvc_tlv, 1650, 50, 0); +static const __maybe_unused DECLARE_TLV_DB_SCALE(tas2770_amp_tlv, 1100, 50, 0); + +#endif diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c index bacc3f6ed4bd..65a48326f5b9 100644 --- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c +++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c @@ -24,6 +24,7 @@ #include <sound/tas2781.h> #include <sound/tas2781-comlib-i2c.h> #include <sound/tlv.h> +#include <sound/tas2770-tlv.h> #include <sound/tas2781-tlv.h>
#include "hda_local.h" @@ -45,9 +46,18 @@ #define TAS2563_CAL_TLIM TASDEVICE_REG(0, 0x10, 0x14) #define TAS2563_CAL_R0 TASDEVICE_REG(0, 0x0f, 0x34)
+enum device_chip_id { + HDA_TAS2563, + HDA_TAS2770, + HDA_TAS2781, + HDA_OTHERS +}; + struct tas2781_hda_i2c_priv { struct snd_kcontrol *snd_ctls[2]; int (*save_calibration)(struct tas2781_hda *h); + + int hda_chip_id; };
static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data) @@ -245,6 +255,15 @@ static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, return change; }
+static const struct snd_kcontrol_new tas2770_snd_controls[] = { + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS2770_AMP_LEVEL, + 0, 0, 20, 0, tas2781_amp_getvol, + tas2781_amp_putvol, tas2770_amp_tlv), + ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2770_DVC_LEVEL, + 0, 0, 31, 0, tas2781_amp_getvol, + tas2781_amp_putvol, tas2770_dvc_tlv), +}; + static const struct snd_kcontrol_new tas2781_snd_controls[] = { ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, 1, 0, 20, 0, tas2781_amp_getvol, @@ -253,7 +272,7 @@ static const struct snd_kcontrol_new tas2781_snd_controls[] = { tas2781_force_fwload_get, tas2781_force_fwload_put), };
-static const struct snd_kcontrol_new tas2781_prof_ctrl = { +static const struct snd_kcontrol_new tasdevice_prof_ctrl = { .name = "Speaker Profile Id", .iface = SNDRV_CTL_ELEM_IFACE_CARD, .info = tasdevice_info_profile, @@ -261,7 +280,7 @@ static const struct snd_kcontrol_new tas2781_prof_ctrl = { .put = tasdevice_set_profile_id, };
-static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl = { +static const struct snd_kcontrol_new tasdevice_dsp_prog_ctrl = { .name = "Speaker Program Id", .iface = SNDRV_CTL_ELEM_IFACE_CARD, .info = tasdevice_info_programs, @@ -269,7 +288,7 @@ static const struct snd_kcontrol_new tas2781_dsp_prog_ctrl = { .put = tasdevice_program_put, };
-static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl = { +static const struct snd_kcontrol_new tasdevice_dsp_conf_ctrl = { .name = "Speaker Config Id", .iface = SNDRV_CTL_ELEM_IFACE_CARD, .info = tasdevice_info_config, @@ -378,44 +397,34 @@ static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda) snd_ctl_remove(codec->card, tas_hda->prof_ctl); }
-static void tasdev_fw_ready(const struct firmware *fmw, void *context) +static void tasdev_add_kcontrols(struct tasdevice_priv *tas_priv, + struct snd_kcontrol **ctls, struct hda_codec *codec, + const struct snd_kcontrol_new *tas_snd_ctrls, int num_ctls) { - struct tasdevice_priv *tas_priv = context; - struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev); - struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv; - struct hda_codec *codec = tas_priv->codec; - int i, ret, spk_id; - - pm_runtime_get_sync(tas_priv->dev); - mutex_lock(&tas_priv->codec_lock); + int i, ret;
- ret = tasdevice_rca_parser(tas_priv, fmw); - if (ret) - goto out; - - tas_hda->prof_ctl = snd_ctl_new1(&tas2781_prof_ctrl, tas_priv); - ret = snd_ctl_add(codec->card, tas_hda->prof_ctl); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_prof_ctrl.name, ret); - goto out; - } - - for (i = 0; i < ARRAY_SIZE(tas2781_snd_controls); i++) { - hda_priv->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_controls[i], - tas_priv); - ret = snd_ctl_add(codec->card, hda_priv->snd_ctls[i]); + for (i = 0; i < num_ctls; i++) { + ctls[i] = snd_ctl_new1( + &tas_snd_ctrls[i], tas_priv); + ret = snd_ctl_add(codec->card, ctls[i]); if (ret) { dev_err(tas_priv->dev, "Failed to add KControl %s = %d\n", - tas2781_snd_controls[i].name, ret); - goto out; + tas_snd_ctrls[i].name, ret); + break; } } +}
- tasdevice_dsp_remove(tas_priv); +static void tasdevice_dspfw_init(void *context) +{ + struct tasdevice_priv *tas_priv = context; + struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev); + struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv; + struct hda_codec *codec = tas_priv->codec; + int ret, spk_id;
+ tasdevice_dsp_remove(tas_priv); tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; if (tas_priv->speaker_id != NULL) { // Speaker id need to be checked for ASUS only. @@ -441,28 +450,12 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) dev_err(tas_priv->dev, "dspfw load %s error\n", tas_priv->coef_binaryname); tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; - goto out; - } - - tas_hda->dsp_prog_ctl = snd_ctl_new1(&tas2781_dsp_prog_ctrl, - tas_priv); - ret = snd_ctl_add(codec->card, tas_hda->dsp_prog_ctl); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_dsp_prog_ctrl.name, ret); - goto out; - } - - tas_hda->dsp_conf_ctl = snd_ctl_new1(&tas2781_dsp_conf_ctrl, - tas_priv); - ret = snd_ctl_add(codec->card, tas_hda->dsp_conf_ctl); - if (ret) { - dev_err(tas_priv->dev, - "Failed to add KControl %s = %d\n", - tas2781_dsp_conf_ctrl.name, ret); - goto out; + return; } + tasdev_add_kcontrols(tas_priv, &tas_hda->dsp_prog_ctl, codec, + &tasdevice_dsp_prog_ctrl, 1); + tasdev_add_kcontrols(tas_priv, &tas_hda->dsp_conf_ctl, codec, + &tasdevice_dsp_conf_ctrl, 1);
tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; tasdevice_prmg_load(tas_priv, 0); @@ -475,9 +468,45 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context) * calibrated data inside algo. */ hda_priv->save_calibration(tas_hda); +} + +static void tasdev_fw_ready(const struct firmware *fmw, void *context) +{ + struct tasdevice_priv *tas_priv = context; + struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev); + struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv; + struct hda_codec *codec = tas_priv->codec; + int ret; + + pm_runtime_get_sync(tas_priv->dev); + mutex_lock(&tas_priv->codec_lock); + + ret = tasdevice_rca_parser(tas_priv, fmw); + if (ret) + goto out;
- tasdevice_tuning_switch(tas_hda->priv, 0); - tas_hda->priv->playback_started = true; + tas_priv->fw_state = TASDEVICE_RCA_FW_OK; + tasdev_add_kcontrols(tas_priv, &tas_hda->prof_ctl, codec, + &tasdevice_prof_ctrl, 1); + + switch (hda_priv->hda_chip_id) { + case HDA_TAS2770: + tasdev_add_kcontrols(tas_priv, hda_priv->snd_ctls, codec, + &tas2770_snd_controls[0], + ARRAY_SIZE(tas2770_snd_controls)); + break; + case HDA_TAS2781: + tasdev_add_kcontrols(tas_priv, hda_priv->snd_ctls, codec, + &tas2781_snd_controls[0], + ARRAY_SIZE(tas2781_snd_controls)); + tasdevice_dspfw_init(context); + break; + case HDA_TAS2563: + tasdevice_dspfw_init(context); + break; + default: + break; + }
out: mutex_unlock(&tas_hda->priv->codec_lock); @@ -581,16 +610,33 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt) return -ENOMEM;
if (strstr(dev_name(&clt->dev), "TIAS2781")) { - device_name = "TIAS2781"; + /* + * TAS2781, integrated on-chip DSP with + * global I2C address supported. + */ + device_name = "TIAS2781"; + hda_priv->hda_chip_id = HDA_TAS2781; hda_priv->save_calibration = tas2781_save_calibration; tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR; + } else if (strstarts(dev_name(&clt->dev), "i2c-TXNW2770")) { + /* + * TAS2770, has no on-chip DSP, so no calibration data + * required; has no global I2C address supported. + */ + device_name = "TXNW2770"; + hda_priv->hda_chip_id = HDA_TAS2770; } else if (strstarts(dev_name(&clt->dev), "i2c-TXNW2781:00-tas2781-hda.0")) { device_name = "TXNW2781"; hda_priv->save_calibration = tas2781_save_calibration; tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR; } else if (strstr(dev_name(&clt->dev), "INT8866")) { - device_name = "INT8866"; + /* + * TAS2563, integrated on-chip DSP with + * global I2C address supported. + */ + device_name = "INT8866"; + hda_priv->hda_chip_id = HDA_TAS2563; hda_priv->save_calibration = tas2563_save_calibration; tas_hda->priv->global_addr = TAS2563_GLOBAL_ADDR; } else { @@ -727,6 +773,7 @@ static const struct i2c_device_id tas2781_hda_i2c_id[] = { static const struct acpi_device_id tas2781_acpi_hda_match[] = { {"INT8866", 0 }, {"TIAS2781", 0 }, + {"TXNW2770", 0 }, {"TXNW2781", 0 }, {} };

On Wed, 23 Jul 2025 16:24:23 +0200, Baojun Xu wrote:
Add TAS2770 support in TI's HDA driver. And add hda_chip_id for more products. Distinguish DSP and non-DSP in firmware loading function.
Signed-off-by: Baojun Xu baojun.xu@ti.com
Applied now, thanks.
BTW, is include/sound/tas2770-tlv.h used by any other driver? (Also include/sound/tas2781-tlv.h).
If those are used only by tas2781-hda-i2s/spi drivers, the files can be moved to sound/hda/codecs/side-codecs as local headers.
In general, include/sound is rather for public headers that are read by multiple drivers in different places.
thanks,
Takashi

From: Takashi Iwai tiwai@suse.de Sent: 23 July 2025 22:40 To: Xu, Baojun Cc: broonie@kernel.org; andriy.shevchenko@linux.intel.com; alsa-devel@alsa-project.org; Ding, Shenghao; 13916275206@139.com; P O, Vijeth; linux-sound@vger.kernel.org; linux-kernel@vger.kernel.org Subject: [EXTERNAL] Re: [PATCH v2] ALSA: hda: Add TAS2770 support
On Wed, 23 Jul 2025 16: 24: 23 +0200, Baojun Xu wrote: > > Add TAS2770 support in TI's HDA driver. And add hda_chip_id for > more products. Distinguish DSP and non-DSP in firmware > loading function. > > Signed-off-by: Baojun 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. https://us-phishalarm-ewt.proofpoint.com/EWT/v1/G3vK!vxdrHf3EPmdQig3vfxtoX0ga-ZW2grb4pJ-90xEaom-nVsHT8xHY3D2nK8v5mdWlEw0t3Q$ Report Suspicious
ZjQcmQRYFpfptBannerEnd
On Wed, 23 Jul 2025 16:24:23 +0200, Baojun Xu wrote:
Add TAS2770 support in TI's HDA driver. And add hda_chip_id for more products. Distinguish DSP and non-DSP in firmware loading function.
Signed-off-by: Baojun Xu baojun.xu@ti.com
Applied now, thanks.
BTW, is include/sound/tas2770-tlv.h used by any other driver? (Also include/sound/tas2781-tlv.h).
If those are used only by tas2781-hda-i2s/spi drivers, the files can be moved to sound/hda/codecs/side-codecs as local headers.
In general, include/sound is rather for public headers that are read by multiple drivers in different places.
Thanks for the apply! Yes, include/sound/tasxxxx-tlv.h will also be used by other drivers (for example, sound/soc/codecs/tas2xxx-i2c.c)
thanks,
Takashi
Best Regards Jim

On Sat, 26 Jul 2025 13:34:28 +0200, Xu, Baojun wrote:
From: Takashi Iwai tiwai@suse.de Sent: 23 July 2025 22:40 To: Xu, Baojun Cc: broonie@kernel.org; andriy.shevchenko@linux.intel.com; alsa-devel@alsa-project.org; Ding, Shenghao; 13916275206@139.com; P O, Vijeth; linux-sound@vger.kernel.org; linux-kernel@vger.kernel.org Subject: [EXTERNAL] Re: [PATCH v2] ALSA: hda: Add TAS2770 support
On Wed, 23 Jul 2025 16: 24: 23 +0200, Baojun Xu wrote: > > Add TAS2770 support in TI's HDA driver. And add hda_chip_id for > more products. Distinguish DSP and non-DSP in firmware > loading function. > > Signed-off-by: Baojun 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. https://us-phishalarm-ewt.proofpoint.com/EWT/v1/G3vK!vxdrHf3EPmdQig3vfxtoX0ga-ZW2grb4pJ-90xEaom-nVsHT8xHY3D2nK8v5mdWlEw0t3Q$ Report Suspicious
ZjQcmQRYFpfptBannerEnd
On Wed, 23 Jul 2025 16:24:23 +0200, Baojun Xu wrote:
Add TAS2770 support in TI's HDA driver. And add hda_chip_id for more products. Distinguish DSP and non-DSP in firmware loading function.
Signed-off-by: Baojun Xu baojun.xu@ti.com
Applied now, thanks.
BTW, is include/sound/tas2770-tlv.h used by any other driver? (Also include/sound/tas2781-tlv.h).
If those are used only by tas2781-hda-i2s/spi drivers, the files can be moved to sound/hda/codecs/side-codecs as local headers.
In general, include/sound is rather for public headers that are read by multiple drivers in different places.
Thanks for the apply! Yes, include/sound/tasxxxx-tlv.h will also be used by other drivers (for example, sound/soc/codecs/tas2xxx-i2c.c)
OK, in that case, it's fine.
thanks,
Takashi
participants (3)
-
Baojun Xu
-
Takashi Iwai
-
Xu, Baojun