ALSA: hda: Add TAS5825 support

Add TAS5825 support in TI's HDA driver.
TAS5825 is an on-chip DSP, but no calibration is required,
and no global address support smart amplifier devices.

Signed-off-by: Baojun Xu <baojun.xu@ti.com>
Acked-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20250810122358.1575-1-baojun.xu@ti.com
This commit is contained in:
Baojun Xu
2025-08-10 20:23:58 +08:00
committed by Takashi Iwai
parent 03714939b1
commit f4ee43ae6e
5 changed files with 121 additions and 5 deletions

View File

@@ -34,6 +34,7 @@
#define PPC3_VERSION_TAS2781_BASIC_MIN 0x14600
#define PPC3_VERSION_TAS2781_ALPHA_MIN 0x4a00
#define PPC3_VERSION_TAS2781_BETA_MIN 0x19400
#define PPC3_VERSION_TAS5825_BASE 0x114200
#define TASDEVICE_DEVICE_SUM 8
#define TASDEVICE_CONFIG_SUM 64
@@ -53,6 +54,8 @@ enum tasdevice_dsp_dev_idx {
TASDEVICE_DSP_TAS_2781_DUAL_MONO,
TASDEVICE_DSP_TAS_2781_21,
TASDEVICE_DSP_TAS_2781_QUAD,
TASDEVICE_DSP_TAS_5825_MONO,
TASDEVICE_DSP_TAS_5825_DUAL,
TASDEVICE_DSP_TAS_MAX_DEVICE
};

View File

@@ -49,9 +49,9 @@
#define TASDEVICE_REG(book, page, reg) (((book * 256 * 128) + \
(page * 128)) + reg)
/* Software Reset */
/* Software Reset, compatble with new device (TAS5825). */
#define TASDEVICE_REG_SWRESET TASDEVICE_REG(0x0, 0x0, 0x01)
#define TASDEVICE_REG_SWRESET_RESET BIT(0)
#define TASDEVICE_REG_SWRESET_RESET (BIT(0) | BIT(4))
/* Checksum */
#define TASDEVICE_CHECKSUM_REG TASDEVICE_REG(0x0, 0x0, 0x7e)

View File

@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0 */
//
// ALSA SoC Texas Instruments TAS5825 Audio Smart Amplifier
//
// Copyright (C) 2025 Texas Instruments Incorporated
// https://www.ti.com
//
// The TAS5825 hda driver implements for one or two TAS5825 chips.
//
// Author: Baojun Xu <baojun.xu@ti.com>
//
#ifndef __TAS5825_TLV_H__
#define __TAS5825_TLV_H__
#define TAS5825_DVC_LEVEL TASDEVICE_REG(0x0, 0x0, 0x4c)
#define TAS5825_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x54)
static const __maybe_unused DECLARE_TLV_DB_SCALE(
tas5825_dvc_tlv, -10300, 50, 0);
static const __maybe_unused DECLARE_TLV_DB_SCALE(
tas5825_amp_tlv, -1550, 50, 0);
#endif

View File

@@ -26,6 +26,7 @@
#include <sound/tlv.h>
#include <sound/tas2770-tlv.h>
#include <sound/tas2781-tlv.h>
#include <sound/tas5825-tlv.h>
#include "hda_local.h"
#include "hda_auto_parser.h"
@@ -50,6 +51,7 @@ enum device_chip_id {
HDA_TAS2563,
HDA_TAS2770,
HDA_TAS2781,
HDA_TAS5825,
HDA_OTHERS
};
@@ -272,6 +274,17 @@ static const struct snd_kcontrol_new tas2781_snd_controls[] = {
tas2781_force_fwload_get, tas2781_force_fwload_put),
};
static const struct snd_kcontrol_new tas5825_snd_controls[] = {
ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS5825_AMP_LEVEL,
0, 0, 31, 1, tas2781_amp_getvol,
tas2781_amp_putvol, tas5825_amp_tlv),
ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS5825_DVC_LEVEL,
0, 0, 254, 1, tas2781_amp_getvol,
tas2781_amp_putvol, tas5825_dvc_tlv),
ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0,
tas2781_force_fwload_get, tas2781_force_fwload_put),
};
static const struct snd_kcontrol_new tasdevice_prof_ctrl = {
.name = "Speaker Profile Id",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
@@ -501,6 +514,12 @@ static void tasdev_fw_ready(const struct firmware *fmw, void *context)
ARRAY_SIZE(tas2781_snd_controls));
tasdevice_dspfw_init(context);
break;
case HDA_TAS5825:
tasdev_add_kcontrols(tas_priv, hda_priv->snd_ctls, codec,
&tas5825_snd_controls[0],
ARRAY_SIZE(tas5825_snd_controls));
tasdevice_dspfw_init(context);
break;
case HDA_TAS2563:
tasdevice_dspfw_init(context);
break;
@@ -628,6 +647,7 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt)
} else if (strstarts(dev_name(&clt->dev),
"i2c-TXNW2781:00-tas2781-hda.0")) {
device_name = "TXNW2781";
hda_priv->hda_chip_id = HDA_TAS2781;
hda_priv->save_calibration = tas2781_save_calibration;
tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR;
} else if (strstr(dev_name(&clt->dev), "INT8866")) {
@@ -639,6 +659,13 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt)
hda_priv->hda_chip_id = HDA_TAS2563;
hda_priv->save_calibration = tas2563_save_calibration;
tas_hda->priv->global_addr = TAS2563_GLOBAL_ADDR;
} else if (strstarts(dev_name(&clt->dev), "i2c-TXNW5825")) {
/*
* TAS5825, integrated on-chip DSP without
* global I2C address and calibration supported.
*/
device_name = "TXNW5825";
hda_priv->hda_chip_id = HDA_TAS5825;
} else {
return -ENODEV;
}
@@ -775,6 +802,7 @@ static const struct acpi_device_id tas2781_acpi_hda_match[] = {
{"TIAS2781", 0 },
{"TXNW2770", 0 },
{"TXNW2781", 0 },
{"TXNW5825", 0 },
{}
};
MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match);

View File

@@ -91,7 +91,7 @@ struct blktyp_devidx_map {
};
static const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = {
1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4
1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4, 1, 2
};
/* fixed m68k compiling issue: mapping table can save code field */
@@ -509,6 +509,56 @@ out:
return offset;
}
static int fw_parse_tas5825_program_data_kernel(
struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw,
const struct firmware *fmw, int offset)
{
struct tasdevice_prog *program;
unsigned int i;
for (i = 0; i < tas_fmw->nr_programs; i++) {
program = &(tas_fmw->programs[i]);
if (offset + 72 > fmw->size) {
dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
return -EINVAL;
}
/* Skip 65 unused byts*/
offset += 65;
offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data),
fmw, offset);
if (offset < 0)
return offset;
}
return offset;
}
static int fw_parse_tas5825_configuration_data_kernel(
struct tasdevice_priv *tas_priv,
struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
{
const unsigned char *data = fmw->data;
struct tasdevice_config *config;
unsigned int i;
for (i = 0; i < tas_fmw->nr_configurations; i++) {
config = &(tas_fmw->configs[i]);
if (offset + 80 > fmw->size) {
dev_err(tas_priv->dev, "%s: mpName error\n", __func__);
return -EINVAL;
}
memcpy(config->name, &data[offset], 64);
/* Skip extra 8 bytes*/
offset += 72;
offset = fw_parse_data_kernel(tas_fmw, &(config->dev_data),
fmw, offset);
if (offset < 0)
return offset;
}
return offset;
}
static int fw_parse_program_data_kernel(
struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw,
const struct firmware *fmw, int offset)
@@ -1826,7 +1876,8 @@ static void dspbin_type_check(struct tasdevice_priv *tas_priv,
else
tas_priv->dspbin_typ = TASDEV_ALPHA;
}
if (tas_priv->dspbin_typ != TASDEV_BASIC)
if ((tas_priv->dspbin_typ != TASDEV_BASIC) &&
(ppcver < PPC3_VERSION_TAS5825_BASE))
tas_priv->fw_parse_fct_param_address =
fw_parse_fct_param_address;
}
@@ -1837,7 +1888,17 @@ static int dspfw_default_callback(struct tasdevice_priv *tas_priv,
int rc = 0;
if (drv_ver == 0x100) {
if (ppcver >= PPC3_VERSION_BASE) {
if (ppcver >= PPC3_VERSION_TAS5825_BASE) {
tas_priv->fw_parse_variable_header =
fw_parse_variable_header_kernel;
tas_priv->fw_parse_program_data =
fw_parse_tas5825_program_data_kernel;
tas_priv->fw_parse_configuration_data =
fw_parse_tas5825_configuration_data_kernel;
tas_priv->tasdevice_load_block =
tasdevice_load_block_kernel;
dspbin_type_check(tas_priv, ppcver);
} else if (ppcver >= PPC3_VERSION_BASE) {
tas_priv->fw_parse_variable_header =
fw_parse_variable_header_kernel;
tas_priv->fw_parse_program_data =