]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ALSA: hda: Add TAS2770 support
authorBaojun Xu <baojun.xu@ti.com>
Wed, 23 Jul 2025 14:24:23 +0000 (22:24 +0800)
committerTakashi Iwai <tiwai@suse.de>
Wed, 23 Jul 2025 14:38:25 +0000 (16:38 +0200)
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>
Link: https://patch.msgid.link/20250723142423.38768-1-baojun.xu@ti.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/sound/tas2770-tlv.h [new file with mode: 0644]
sound/hda/codecs/side-codecs/tas2781_hda_i2c.c

diff --git a/include/sound/tas2770-tlv.h b/include/sound/tas2770-tlv.h
new file mode 100644 (file)
index 0000000..c0bd495
--- /dev/null
@@ -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
index bacc3f6ed4bd989ad658e93662602439685f7ef9..a0b1326818041217243005f9fc1875453bb6f7f2 100644 (file)
@@ -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"
 #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")) {
+               /*
+                * 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")) {
+               /*
+                * 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 },
        {}
 };